feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
162
jdkSrc/jdk8/jdk/jfr/internal/ASMToolkit.java
Normal file
162
jdkSrc/jdk8/jdk/jfr/internal/ASMToolkit.java
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.EventInstrumentation.FieldInfo;
|
||||
|
||||
final class ASMToolkit {
|
||||
private static Type TYPE_STRING = Type.getType(String.class);
|
||||
private static Type Type_THREAD = Type.getType(Thread.class);
|
||||
private static Type TYPE_CLASS = Type.getType(Class.class);
|
||||
|
||||
public static void invokeSpecial(MethodVisitor methodVisitor, String className, Method m) {
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, m.getName(), m.getDescriptor(), false);
|
||||
}
|
||||
|
||||
public static void invokeStatic(MethodVisitor methodVisitor, String className, Method m) {
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, m.getName(), m.getDescriptor(), false);
|
||||
}
|
||||
|
||||
public static void invokeVirtual(MethodVisitor methodVisitor, String className, Method m) {
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, m.getName(), m.getDescriptor(), false);
|
||||
}
|
||||
|
||||
|
||||
public static Type toType(ValueDescriptor v) {
|
||||
String typeName = v.getTypeName();
|
||||
|
||||
switch (typeName) {
|
||||
case "byte":
|
||||
return Type.BYTE_TYPE;
|
||||
case "short":
|
||||
return Type.SHORT_TYPE;
|
||||
case "int":
|
||||
return Type.INT_TYPE;
|
||||
case "long":
|
||||
return Type.LONG_TYPE;
|
||||
case "double":
|
||||
return Type.DOUBLE_TYPE;
|
||||
case "float":
|
||||
return Type.FLOAT_TYPE;
|
||||
case "char":
|
||||
return Type.CHAR_TYPE;
|
||||
case "boolean":
|
||||
return Type.BOOLEAN_TYPE;
|
||||
case "java.lang.String":
|
||||
return TYPE_STRING;
|
||||
case "java.lang.Thread":
|
||||
return Type_THREAD;
|
||||
case "java.lang.Class":
|
||||
return TYPE_CLASS;
|
||||
}
|
||||
// Add support for SettingControl?
|
||||
throw new Error("Not a valid type " + v.getTypeName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts "int" into "I" and "java.lang.String" into "Ljava/lang/String;"
|
||||
*
|
||||
* @param typeName
|
||||
* type
|
||||
*
|
||||
* @return descriptor
|
||||
*/
|
||||
public static String getDescriptor(String typeName) {
|
||||
if ("int".equals(typeName)) {
|
||||
return "I";
|
||||
}
|
||||
if ("long".equals(typeName)) {
|
||||
return "J";
|
||||
}
|
||||
if ("boolean".equals(typeName)) {
|
||||
return "Z";
|
||||
}
|
||||
if ("float".equals(typeName)) {
|
||||
return "F";
|
||||
}
|
||||
if ("double".equals(typeName)) {
|
||||
return "D";
|
||||
}
|
||||
if ("short".equals(typeName)) {
|
||||
return "S";
|
||||
}
|
||||
if ("char".equals(typeName)) {
|
||||
return "C";
|
||||
}
|
||||
if ("byte".equals(typeName)) {
|
||||
return "B";
|
||||
}
|
||||
String internal = getInternalName(typeName);
|
||||
return Type.getObjectType(internal).getDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts java.lang.String into java/lang/String
|
||||
*
|
||||
* @param className
|
||||
*
|
||||
* @return internal name
|
||||
*/
|
||||
public static String getInternalName(String className) {
|
||||
return className.replace(".", "/");
|
||||
}
|
||||
|
||||
public static Method makeWriteMethod(List<FieldInfo> fields) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (FieldInfo v : fields) {
|
||||
if (!v.fieldName.equals(EventInstrumentation.FIELD_EVENT_THREAD) && !v.fieldName.equals(EventInstrumentation.FIELD_STACK_TRACE)) {
|
||||
sb.append(v.fieldDescriptor);
|
||||
}
|
||||
}
|
||||
sb.append(")V");
|
||||
return new Method("write", sb.toString());
|
||||
}
|
||||
|
||||
public static void logASM(String className, byte[] bytes) {
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Generated bytecode for class " + className);
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE, () -> {
|
||||
ClassReader cr = new ClassReader(bytes);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintWriter w = new PrintWriter(baos);
|
||||
w.println("Bytecode:");
|
||||
cr.accept(new TraceClassVisitor(w), 0);
|
||||
return baos.toString();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
136
jdkSrc/jdk8/jdk/jfr/internal/AnnotationConstruct.java
Normal file
136
jdkSrc/jdk8/jdk/jfr/internal/AnnotationConstruct.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.Unsigned;
|
||||
|
||||
public final class AnnotationConstruct {
|
||||
|
||||
private static final class AnnotationInvokationHandler implements InvocationHandler {
|
||||
|
||||
private final AnnotationElement annotationElement;
|
||||
|
||||
AnnotationInvokationHandler(AnnotationElement a) {
|
||||
this.annotationElement = a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
int parameters = method.getTypeParameters().length;
|
||||
if (parameters == 0 && annotationElement.hasValue(methodName)) {
|
||||
return annotationElement.getValue(methodName);
|
||||
}
|
||||
throw new UnsupportedOperationException("Flight Recorder proxy only supports members declared in annotation interfaces, i.e. not toString, equals etc.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<AnnotationElement> annotationElements = Collections.emptyList();
|
||||
private byte unsignedFlag = -1;
|
||||
public AnnotationConstruct(List<AnnotationElement> ann) {
|
||||
this.annotationElements = ann;
|
||||
}
|
||||
|
||||
public AnnotationConstruct() {
|
||||
}
|
||||
|
||||
public void setAnnotationElements(List<AnnotationElement> elements) {
|
||||
annotationElements = Utils.smallUnmodifiable(elements);
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
Label label = getAnnotation(Label.class);
|
||||
if (label == null) {
|
||||
return null;
|
||||
}
|
||||
return label.value();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
Description description = getAnnotation(Description.class);
|
||||
if (description == null) {
|
||||
return null;
|
||||
}
|
||||
return description.value();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <T> T getAnnotation(Class<? extends Annotation> clazz) {
|
||||
AnnotationElement ae = getAnnotationElement(clazz);
|
||||
if (ae != null) {
|
||||
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, new AnnotationInvokationHandler(ae));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<AnnotationElement> getUnmodifiableAnnotationElements() {
|
||||
return annotationElements;
|
||||
}
|
||||
|
||||
// package private
|
||||
boolean remove(AnnotationElement annotation) {
|
||||
return annotationElements.remove(annotation);
|
||||
}
|
||||
|
||||
private AnnotationElement getAnnotationElement(Class<? extends Annotation> clazz) {
|
||||
// if multiple annotation elements with the same name exists, prioritize
|
||||
// the one with the same id. Note, id alone is not a guarantee, since it
|
||||
// may differ between JVM instances.
|
||||
long id = Type.getTypeId(clazz);
|
||||
String className = clazz.getName();
|
||||
for (AnnotationElement a : getUnmodifiableAnnotationElements()) {
|
||||
if (a.getTypeId() == id && a.getTypeName().equals(className)) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
for (AnnotationElement a : getUnmodifiableAnnotationElements()) {
|
||||
if (a.getTypeName().equals(className)) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasUnsigned() {
|
||||
// Must be initialized lazily since some annotation elements
|
||||
// are added after construction
|
||||
if (unsignedFlag < 0) {
|
||||
Unsigned unsigned = getAnnotation(Unsigned.class);
|
||||
unsignedFlag = (byte) (unsigned == null ? 0 :1);
|
||||
}
|
||||
return unsignedFlag == (byte)1 ? true : false;
|
||||
}
|
||||
}
|
||||
227
jdkSrc/jdk8/jdk/jfr/internal/Bits.java
Normal file
227
jdkSrc/jdk8/jdk/jfr/internal/Bits.java
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
final class Bits { // package-private
|
||||
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
// XXX TODO proper value (e.g. copy from java.nio.Bits)
|
||||
private static final boolean unalignedAccess = false/*unsafe.unalignedAccess()*/;
|
||||
private static final boolean bigEndian = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
|
||||
|
||||
private Bits() { }
|
||||
|
||||
// -- Swapping --
|
||||
|
||||
private static short swap(short x) {
|
||||
return Short.reverseBytes(x);
|
||||
}
|
||||
|
||||
private static char swap(char x) {
|
||||
return Character.reverseBytes(x);
|
||||
}
|
||||
|
||||
private static int swap(int x) {
|
||||
return Integer.reverseBytes(x);
|
||||
}
|
||||
|
||||
private static long swap(long x) {
|
||||
return Long.reverseBytes(x);
|
||||
}
|
||||
|
||||
private static float swap(float x) {
|
||||
return Float.intBitsToFloat(swap(Float.floatToIntBits(x)));
|
||||
}
|
||||
|
||||
private static double swap(double x) {
|
||||
return Double.longBitsToDouble(swap(Double.doubleToLongBits(x)));
|
||||
}
|
||||
|
||||
// -- Alignment --
|
||||
|
||||
private static boolean isAddressAligned(long a, int datumSize) {
|
||||
return (a & datumSize - 1) == 0;
|
||||
}
|
||||
|
||||
// -- Primitives stored per byte
|
||||
|
||||
private static byte char1(char x) { return (byte)(x >> 8); }
|
||||
private static byte char0(char x) { return (byte)(x ); }
|
||||
|
||||
private static byte short1(short x) { return (byte)(x >> 8); }
|
||||
private static byte short0(short x) { return (byte)(x ); }
|
||||
|
||||
private static byte int3(int x) { return (byte)(x >> 24); }
|
||||
private static byte int2(int x) { return (byte)(x >> 16); }
|
||||
private static byte int1(int x) { return (byte)(x >> 8); }
|
||||
private static byte int0(int x) { return (byte)(x ); }
|
||||
|
||||
private static byte long7(long x) { return (byte)(x >> 56); }
|
||||
private static byte long6(long x) { return (byte)(x >> 48); }
|
||||
private static byte long5(long x) { return (byte)(x >> 40); }
|
||||
private static byte long4(long x) { return (byte)(x >> 32); }
|
||||
private static byte long3(long x) { return (byte)(x >> 24); }
|
||||
private static byte long2(long x) { return (byte)(x >> 16); }
|
||||
private static byte long1(long x) { return (byte)(x >> 8); }
|
||||
private static byte long0(long x) { return (byte)(x ); }
|
||||
|
||||
private static void putCharBigEndianUnaligned(long a, char x) {
|
||||
putByte_(a , char1(x));
|
||||
putByte_(a + 1, char0(x));
|
||||
}
|
||||
|
||||
private static void putShortBigEndianUnaligned(long a, short x) {
|
||||
putByte_(a , short1(x));
|
||||
putByte_(a + 1, short0(x));
|
||||
}
|
||||
|
||||
private static void putIntBigEndianUnaligned(long a, int x) {
|
||||
putByte_(a , int3(x));
|
||||
putByte_(a + 1, int2(x));
|
||||
putByte_(a + 2, int1(x));
|
||||
putByte_(a + 3, int0(x));
|
||||
}
|
||||
|
||||
private static void putLongBigEndianUnaligned(long a, long x) {
|
||||
putByte_(a , long7(x));
|
||||
putByte_(a + 1, long6(x));
|
||||
putByte_(a + 2, long5(x));
|
||||
putByte_(a + 3, long4(x));
|
||||
putByte_(a + 4, long3(x));
|
||||
putByte_(a + 5, long2(x));
|
||||
putByte_(a + 6, long1(x));
|
||||
putByte_(a + 7, long0(x));
|
||||
}
|
||||
|
||||
private static void putFloatBigEndianUnaligned(long a, float x) {
|
||||
putIntBigEndianUnaligned(a, Float.floatToRawIntBits(x));
|
||||
}
|
||||
|
||||
private static void putDoubleBigEndianUnaligned(long a, double x) {
|
||||
putLongBigEndianUnaligned(a, Double.doubleToRawLongBits(x));
|
||||
}
|
||||
|
||||
private static void putByte_(long a, byte b) {
|
||||
unsafe.putByte(a, b);
|
||||
}
|
||||
|
||||
private static void putBoolean_(long a, boolean x) {
|
||||
unsafe.putBoolean(null, a, x);
|
||||
}
|
||||
|
||||
private static void putChar_(long a, char x) {
|
||||
unsafe.putChar(a, bigEndian ? x : swap(x));
|
||||
}
|
||||
|
||||
private static void putShort_(long a, short x) {
|
||||
unsafe.putShort(a, bigEndian ? x : swap(x));
|
||||
}
|
||||
|
||||
private static void putInt_(long a, int x) {
|
||||
unsafe.putInt(a, bigEndian ? x : swap(x));
|
||||
}
|
||||
|
||||
private static void putLong_(long a, long x) {
|
||||
unsafe.putLong(a, bigEndian ? x : swap(x));
|
||||
}
|
||||
|
||||
private static void putFloat_(long a, float x) {
|
||||
unsafe.putFloat(a, bigEndian ? x : swap(x));
|
||||
}
|
||||
|
||||
private static void putDouble_(long a, double x) {
|
||||
unsafe.putDouble(a, bigEndian ? x : swap(x));
|
||||
}
|
||||
|
||||
// external api
|
||||
static int putByte(long a, byte x) {
|
||||
putByte_(a, x);
|
||||
return Byte.BYTES;
|
||||
}
|
||||
|
||||
static int putBoolean(long a, boolean x) {
|
||||
putBoolean_(a, x);
|
||||
return Byte.BYTES;
|
||||
}
|
||||
|
||||
static int putChar(long a, char x) {
|
||||
if (unalignedAccess || isAddressAligned(a, Character.BYTES)) {
|
||||
putChar_(a, x);
|
||||
return Character.BYTES;
|
||||
}
|
||||
putCharBigEndianUnaligned(a, x);
|
||||
return Character.BYTES;
|
||||
}
|
||||
|
||||
static int putShort(long a, short x) {
|
||||
if (unalignedAccess || isAddressAligned(a, Short.BYTES)) {
|
||||
putShort_(a, x);
|
||||
return Short.BYTES;
|
||||
}
|
||||
putShortBigEndianUnaligned(a, x);
|
||||
return Short.BYTES;
|
||||
}
|
||||
|
||||
static int putInt(long a, int x) {
|
||||
if (unalignedAccess || isAddressAligned(a, Integer.BYTES)) {
|
||||
putInt_(a, x);
|
||||
return Integer.BYTES;
|
||||
}
|
||||
putIntBigEndianUnaligned(a, x);
|
||||
return Integer.BYTES;
|
||||
}
|
||||
|
||||
static int putLong(long a, long x) {
|
||||
if (unalignedAccess || isAddressAligned(a, Long.BYTES)) {
|
||||
putLong_(a, x);
|
||||
return Long.BYTES;
|
||||
}
|
||||
putLongBigEndianUnaligned(a, x);
|
||||
return Long.BYTES;
|
||||
}
|
||||
|
||||
static int putFloat(long a, float x) {
|
||||
if (unalignedAccess || isAddressAligned(a, Float.BYTES)) {
|
||||
putFloat_(a, x);
|
||||
return Float.BYTES;
|
||||
}
|
||||
putFloatBigEndianUnaligned(a, x);
|
||||
return Float.BYTES;
|
||||
}
|
||||
|
||||
static int putDouble(long a, double x) {
|
||||
if (unalignedAccess || isAddressAligned(a, Double.BYTES)) {
|
||||
putDouble_(a, x);
|
||||
return Double.BYTES;
|
||||
}
|
||||
putDoubleBigEndianUnaligned(a, x);
|
||||
return Double.BYTES;
|
||||
}
|
||||
}
|
||||
116
jdkSrc/jdk8/jdk/jfr/internal/ChunkInputStream.java
Normal file
116
jdkSrc/jdk8/jdk/jfr/internal/ChunkInputStream.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 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;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
final class ChunkInputStream extends InputStream {
|
||||
private final Iterator<RepositoryChunk> chunks;
|
||||
private RepositoryChunk currentChunk;
|
||||
private InputStream stream;
|
||||
|
||||
ChunkInputStream(List<RepositoryChunk> chunks) throws IOException {
|
||||
List<RepositoryChunk> l = new ArrayList<>(chunks.size());
|
||||
for (RepositoryChunk c : chunks) {
|
||||
c.use(); // keep alive while we're reading.
|
||||
l.add(c);
|
||||
}
|
||||
|
||||
this.chunks = l.iterator();
|
||||
nextStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
if (stream != null) {
|
||||
return stream.available();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean nextStream() throws IOException {
|
||||
if (!nextChunk()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = new BufferedInputStream(SecuritySupport.newFileInputStream(currentChunk.getFile()));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean nextChunk() {
|
||||
if (!chunks.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
currentChunk = chunks.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
while (true) {
|
||||
if (stream != null) {
|
||||
int r = stream.read();
|
||||
if (r != -1) {
|
||||
return r;
|
||||
}
|
||||
stream.close();
|
||||
currentChunk.release();
|
||||
stream = null;
|
||||
currentChunk = null;
|
||||
}
|
||||
if (!nextStream()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
stream = null;
|
||||
}
|
||||
while (currentChunk != null) {
|
||||
currentChunk.release();
|
||||
currentChunk = null;
|
||||
if (!nextChunk()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
close();
|
||||
}
|
||||
}
|
||||
148
jdkSrc/jdk8/jdk/jfr/internal/ChunksChannel.java
Normal file
148
jdkSrc/jdk8/jdk/jfr/internal/ChunksChannel.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
final class ChunksChannel implements ReadableByteChannel {
|
||||
private final Iterator<RepositoryChunk> chunks;
|
||||
private RepositoryChunk current;
|
||||
private ReadableByteChannel channel;
|
||||
|
||||
public ChunksChannel(List<RepositoryChunk> chunks) throws IOException {
|
||||
if (chunks.isEmpty()) {
|
||||
throw new FileNotFoundException("No chunks");
|
||||
}
|
||||
List<RepositoryChunk> l = new ArrayList<>(chunks.size());
|
||||
for (RepositoryChunk c : chunks) {
|
||||
c.use(); // keep alive while we're reading.
|
||||
l.add(c);
|
||||
}
|
||||
this.chunks = l.iterator();
|
||||
nextChannel();
|
||||
}
|
||||
|
||||
private boolean nextChunk() {
|
||||
if (!chunks.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
current = chunks.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean nextChannel() throws IOException {
|
||||
if (!nextChunk()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
channel = current.newChannel();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
for (;;) {
|
||||
if (channel != null) {
|
||||
assert current != null;
|
||||
int r = channel.read(dst);
|
||||
if (r != -1) {
|
||||
return r;
|
||||
}
|
||||
channel.close();
|
||||
current.release();
|
||||
channel = null;
|
||||
current = null;
|
||||
}
|
||||
if (!nextChannel()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long transferTo(FileChannel out) throws IOException {
|
||||
long pos = 0;
|
||||
for (;;) {
|
||||
if (channel != null) {
|
||||
assert current != null;
|
||||
|
||||
long rem = current.getSize();
|
||||
while (rem > 0) {
|
||||
long n = Math.min(rem, 1024 * 1024);
|
||||
long w = out.transferFrom(channel, pos, n);
|
||||
// Prevent endless loop
|
||||
if (w == 0) {
|
||||
return out.size();
|
||||
}
|
||||
pos += w;
|
||||
rem -= w;
|
||||
}
|
||||
|
||||
channel.close();
|
||||
current.release();
|
||||
|
||||
channel = null;
|
||||
current = null;
|
||||
}
|
||||
if (!nextChannel()) {
|
||||
return out.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
channel = null;
|
||||
}
|
||||
while (current != null) {
|
||||
current.release();
|
||||
current = null;
|
||||
if (!nextChunk()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return channel != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
close();
|
||||
}
|
||||
}
|
||||
209
jdkSrc/jdk8/jdk/jfr/internal/Control.java
Normal file
209
jdkSrc/jdk8/jdk/jfr/internal/Control.java
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
// User must never be able to subclass directly.
|
||||
//
|
||||
// Never put Control or Setting Control in a collections
|
||||
// so overridable versions of hashCode or equals are
|
||||
// executed in the wrong context. TODO: wrap this class
|
||||
// in SsecureControl directly when it is instantiated and
|
||||
// forward calls using AccessControlContext
|
||||
abstract public class Control {
|
||||
private final AccessControlContext context;
|
||||
private final static int CACHE_SIZE = 5;
|
||||
private final Set<?>[] cachedUnions = new HashSet<?>[CACHE_SIZE];
|
||||
private final String[] cachedValues = new String[CACHE_SIZE];
|
||||
private String defaultValue;
|
||||
private String lastValue;
|
||||
|
||||
// called by exposed subclass in external API
|
||||
public Control(AccessControlContext acc) {
|
||||
Objects.requireNonNull(acc);
|
||||
this.context = acc;
|
||||
|
||||
}
|
||||
|
||||
// only to be called by trusted VM code
|
||||
public Control(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
// For user code to override, must never be called from jdk.jfr.internal
|
||||
// for user defined settings
|
||||
public abstract String combine(Set<String> values);
|
||||
|
||||
// For user code to override, must never be called from jdk.jfr.internal
|
||||
// for user defined settings
|
||||
public abstract void setValue(String value);
|
||||
|
||||
// For user code to override, must never be called from jdk.jfr.internal
|
||||
// for user defined settings
|
||||
public abstract String getValue();
|
||||
|
||||
// Package private, user code should not have access to this method
|
||||
final void apply(Set<String> values) {
|
||||
setValueSafe(findCombineSafe(values));
|
||||
}
|
||||
|
||||
// Package private, user code should not have access to this method.
|
||||
// Only called during event registration
|
||||
final void setDefault() {
|
||||
if (defaultValue == null) {
|
||||
defaultValue = getValueSafe();
|
||||
}
|
||||
apply(defaultValue);
|
||||
}
|
||||
|
||||
final String getValueSafe() {
|
||||
if (context == null) {
|
||||
// VM events requires no access control context
|
||||
return getValue();
|
||||
} else {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
try {
|
||||
return getValue();
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when trying to get value for " + getClass());
|
||||
}
|
||||
return defaultValue != null ? defaultValue : ""; // Need to return something
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void apply(String value) {
|
||||
if (lastValue != null && Objects.equals(value, lastValue)) {
|
||||
return;
|
||||
}
|
||||
setValueSafe(value);
|
||||
}
|
||||
|
||||
final void setValueSafe(String value) {
|
||||
if (context == null) {
|
||||
// VM events requires no access control context
|
||||
try {
|
||||
setValue(value);
|
||||
} catch (Throwable t) {
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass());
|
||||
}
|
||||
} else {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
try {
|
||||
setValue(value);
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
lastValue = value;
|
||||
}
|
||||
|
||||
|
||||
private String combineSafe(Set<String> values) {
|
||||
if (context == null) {
|
||||
// VM events requires no access control context
|
||||
return combine(values);
|
||||
}
|
||||
return AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
try {
|
||||
combine(Collections.unmodifiableSet(values));
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when combining " + values + " for " + getClass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
|
||||
private final String findCombineSafe(Set<String> values) {
|
||||
if (values.size() == 1) {
|
||||
return values.iterator().next();
|
||||
}
|
||||
for (int i = 0; i < CACHE_SIZE; i++) {
|
||||
if (Objects.equals(cachedUnions[i], values)) {
|
||||
return cachedValues[i];
|
||||
}
|
||||
}
|
||||
String result = combineSafe(values);
|
||||
for (int i = 0; i < CACHE_SIZE - 1; i++) {
|
||||
cachedUnions[i + 1] = cachedUnions[i];
|
||||
cachedValues[i + 1] = cachedValues[i];
|
||||
}
|
||||
cachedValues[0] = result;
|
||||
cachedUnions[0] = values;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// package private, user code should not have access to this method
|
||||
final String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// package private, user code should not have access to this method
|
||||
final String getLastValue() {
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
// Precaution to prevent a malicious user from instantiating instances
|
||||
// of a control where the context has not been set up.
|
||||
@Override
|
||||
public final Object clone() throws java.lang.CloneNotSupportedException {
|
||||
throw new CloneNotSupportedException();
|
||||
}
|
||||
|
||||
private final void writeObject(ObjectOutputStream out) throws IOException {
|
||||
throw new IOException("Object cannot be serialized");
|
||||
}
|
||||
|
||||
private final void readObject(ObjectInputStream in) throws IOException {
|
||||
throw new IOException("Class cannot be deserialized");
|
||||
}
|
||||
}
|
||||
75
jdkSrc/jdk8/jdk/jfr/internal/Cutoff.java
Normal file
75
jdkSrc/jdk8/jdk/jfr/internal/Cutoff.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.internal;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
|
||||
/**
|
||||
* Event annotation, determines the cutoff above which an event should not be
|
||||
* recorded, i.e. {@code "20 ms"}.
|
||||
*
|
||||
* This settings is only supported for JVM events,
|
||||
*
|
||||
* @Since 8
|
||||
*/
|
||||
@MetadataDefinition
|
||||
@Target({ ElementType.TYPE })
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Cutoff {
|
||||
/**
|
||||
* Settings name {@code "cutoff"} for configuring event cutoffs.
|
||||
*/
|
||||
public final static String NAME = "cutoff";
|
||||
public final static String INIFITY = "infinity";
|
||||
|
||||
/**
|
||||
* Cutoff, for example {@code "20 ms"}.
|
||||
* <p>
|
||||
* String representation of a positive {@code Long} value followed by an empty
|
||||
* space and one of the following units<br>
|
||||
* <br>
|
||||
* {@code "ns"} (nanoseconds)<br>
|
||||
* {@code "us"} (microseconds)<br>
|
||||
* {@code "ms"} (milliseconds)<br>
|
||||
* {@code "s"} (seconds)<br>
|
||||
* {@code "m"} (minutes)<br>
|
||||
* {@code "h"} (hours)<br>
|
||||
* {@code "d"} (days)<br>
|
||||
* <p>
|
||||
* Example values, {@code "0 ns"}, {@code "10 ms"} and {@code "1 s"}. If the
|
||||
* events has an infinite timespan, the text {@code"infinity"} should be used.
|
||||
*
|
||||
* @return the threshold, default {@code "0 ns"} not {@code null}
|
||||
*/
|
||||
String value() default "inifity";
|
||||
}
|
||||
142
jdkSrc/jdk8/jdk/jfr/internal/EventClassBuilder.java
Normal file
142
jdkSrc/jdk8/jdk/jfr/internal/EventClassBuilder.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;
|
||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
|
||||
|
||||
// Helper class for building dynamic events
|
||||
public final class EventClassBuilder {
|
||||
|
||||
private static final Type TYPE_EVENT = Type.getType(Event.class);
|
||||
private static final Type TYPE_IOBE = Type.getType(IndexOutOfBoundsException.class);
|
||||
private static final Method DEFAULT_CONSTRUCTOR = Method.getMethod("void <init> ()");
|
||||
private static final Method SET_METHOD = Method.getMethod("void set (int, java.lang.Object)");
|
||||
private static final AtomicLong idCounter = new AtomicLong();
|
||||
private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
||||
private final String fullClassName;
|
||||
private final Type type;
|
||||
private final List<ValueDescriptor> fields;
|
||||
private final List<AnnotationElement> annotationElements;
|
||||
|
||||
public EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) {
|
||||
this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet();
|
||||
this.type = Type.getType(fullClassName.replace(".", "/"));
|
||||
this.fields = fields;
|
||||
this.annotationElements = annotationElements;
|
||||
}
|
||||
|
||||
public Class<? extends Event> build() {
|
||||
buildClassInfo();
|
||||
buildConstructor();
|
||||
buildFields();
|
||||
buildSetMethod();
|
||||
endClass();
|
||||
byte[] bytes = classWriter.toByteArray();
|
||||
ASMToolkit.logASM(fullClassName, bytes);
|
||||
return SecuritySupport.defineClass(type.getInternalName(), bytes, Event.class.getClassLoader()).asSubclass(Event.class);
|
||||
}
|
||||
|
||||
private void endClass() {
|
||||
classWriter.visitEnd();
|
||||
}
|
||||
|
||||
private void buildSetMethod() {
|
||||
GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, SET_METHOD, null, null, classWriter);
|
||||
int index = 0;
|
||||
for (ValueDescriptor v : fields) {
|
||||
ga.loadArg(0);
|
||||
ga.visitLdcInsn(index);
|
||||
Label notEqual = new Label();
|
||||
ga.ifICmp(GeneratorAdapter.NE, notEqual);
|
||||
ga.loadThis();
|
||||
ga.loadArg(1);
|
||||
Type fieldType = ASMToolkit.toType(v);
|
||||
ga.unbox(ASMToolkit.toType(v));
|
||||
ga.putField(type, v.getName(), fieldType);
|
||||
ga.visitInsn(Opcodes.RETURN);
|
||||
ga.visitLabel(notEqual);
|
||||
index++;
|
||||
}
|
||||
ga.throwException(TYPE_IOBE, "Index must between 0 and " + fields.size());
|
||||
ga.endMethod();
|
||||
}
|
||||
|
||||
private void buildConstructor() {
|
||||
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), null, null);
|
||||
mv.visitIntInsn(Opcodes.ALOAD, 0);
|
||||
ASMToolkit.invokeSpecial(mv, TYPE_EVENT.getInternalName(), DEFAULT_CONSTRUCTOR);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
}
|
||||
|
||||
private void buildClassInfo() {
|
||||
String internalSuperName = ASMToolkit.getInternalName(Event.class.getName());
|
||||
String internalClassName = type.getInternalName();
|
||||
classWriter.visit(52, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
|
||||
|
||||
for (AnnotationElement a : annotationElements) {
|
||||
String descriptor = ASMToolkit.getDescriptor(a.getTypeName());
|
||||
AnnotationVisitor av = classWriter.visitAnnotation(descriptor, true);
|
||||
for (ValueDescriptor v : a.getValueDescriptors()) {
|
||||
Object value = a.getValue(v.getName());
|
||||
String name = v.getName();
|
||||
if (v.isArray()) {
|
||||
AnnotationVisitor arrayVisitor = av.visitArray(name);
|
||||
Object[] array = (Object[]) value;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
arrayVisitor.visit(null, array[i]);
|
||||
}
|
||||
arrayVisitor.visitEnd();
|
||||
} else {
|
||||
av.visit(name, value);
|
||||
}
|
||||
}
|
||||
av.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void buildFields() {
|
||||
for (ValueDescriptor v : fields) {
|
||||
String internal = ASMToolkit.getDescriptor(v.getTypeName());
|
||||
classWriter.visitField(Opcodes.ACC_PRIVATE, v.getName(), internal, null, null);
|
||||
// No need to store annotations on field since they will be replaced anyway.
|
||||
}
|
||||
}
|
||||
}
|
||||
291
jdkSrc/jdk8/jdk/jfr/internal/EventControl.java
Normal file
291
jdkSrc/jdk8/jdk/jfr/internal/EventControl.java
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Enabled;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.Period;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.SettingDefinition;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.Threshold;
|
||||
import jdk.jfr.events.ActiveSettingEvent;
|
||||
import jdk.jfr.internal.EventInstrumentation.SettingInfo;
|
||||
import jdk.jfr.internal.settings.CutoffSetting;
|
||||
import jdk.jfr.internal.settings.EnabledSetting;
|
||||
import jdk.jfr.internal.settings.PeriodSetting;
|
||||
import jdk.jfr.internal.settings.StackTraceSetting;
|
||||
import jdk.jfr.internal.settings.ThresholdSetting;
|
||||
|
||||
// This class can't have a hard reference from PlatformEventType, since it
|
||||
// holds SettingControl instances that need to be released
|
||||
// when a class is unloaded (to avoid memory leaks).
|
||||
public final class EventControl {
|
||||
|
||||
static final String FIELD_SETTING_PREFIX = "setting";
|
||||
private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class);
|
||||
private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class);
|
||||
private static final Type TYPE_STACK_TRACE = TypeLibrary.createType(StackTraceSetting.class);
|
||||
private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class);
|
||||
private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class);
|
||||
|
||||
private final List<SettingInfo> settingInfos = new ArrayList<>();
|
||||
private final Map<String, Control> eventControls = new HashMap<>(5);
|
||||
private final PlatformEventType type;
|
||||
private final String idName;
|
||||
|
||||
EventControl(PlatformEventType eventType) {
|
||||
eventControls.put(Enabled.NAME, defineEnabled(eventType));
|
||||
if (eventType.hasDuration()) {
|
||||
eventControls.put(Threshold.NAME, defineThreshold(eventType));
|
||||
}
|
||||
if (eventType.hasStackTrace()) {
|
||||
eventControls.put(StackTrace.NAME, defineStackTrace(eventType));
|
||||
}
|
||||
if (eventType.hasPeriod()) {
|
||||
eventControls.put(Period.NAME, definePeriod(eventType));
|
||||
}
|
||||
if (eventType.hasCutoff()) {
|
||||
eventControls.put(Cutoff.NAME, defineCutoff(eventType));
|
||||
}
|
||||
|
||||
ArrayList<AnnotationElement> aes = new ArrayList<>(eventType.getAnnotationElements());
|
||||
remove(eventType, aes, Threshold.class);
|
||||
remove(eventType, aes, Period.class);
|
||||
remove(eventType, aes, Enabled.class);
|
||||
remove(eventType, aes, StackTrace.class);
|
||||
remove(eventType, aes, Cutoff.class);
|
||||
aes.trimToSize();
|
||||
eventType.setAnnotations(aes);
|
||||
this.type = eventType;
|
||||
this.idName = String.valueOf(eventType.getId());
|
||||
}
|
||||
|
||||
static void remove(PlatformEventType type, List<AnnotationElement> aes, Class<? extends java.lang.annotation.Annotation> clazz) {
|
||||
long id = Type.getTypeId(clazz);
|
||||
for (AnnotationElement a : type.getAnnotationElements()) {
|
||||
if (a.getTypeId() == id && a.getTypeName().equals(clazz.getName())) {
|
||||
aes.remove(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventControl(PlatformEventType es, Class<? extends Event> eventClass) {
|
||||
this(es);
|
||||
defineSettings(eventClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void defineSettings(Class<?> eventClass) {
|
||||
// Iterate up the class hierarchy and let
|
||||
// subclasses shadow base classes.
|
||||
boolean allowPrivateMethod = true;
|
||||
while (eventClass != null) {
|
||||
for (Method m : eventClass.getDeclaredMethods()) {
|
||||
boolean isPrivate = Modifier.isPrivate(m.getModifiers());
|
||||
if (m.getReturnType() == Boolean.TYPE && m.getParameterCount() == 1 && (!isPrivate || allowPrivateMethod)) {
|
||||
SettingDefinition se = m.getDeclaredAnnotation(SettingDefinition.class);
|
||||
if (se != null) {
|
||||
Class<?> settingClass = m.getParameters()[0].getType();
|
||||
if (!Modifier.isAbstract(settingClass.getModifiers()) && SettingControl.class.isAssignableFrom(settingClass)) {
|
||||
String name = m.getName();
|
||||
Name n = m.getAnnotation(Name.class);
|
||||
if (n != null) {
|
||||
name = n.value();
|
||||
}
|
||||
if (!eventControls.containsKey(name)) {
|
||||
defineSetting((Class<? extends SettingControl>) settingClass, m, type, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eventClass = eventClass.getSuperclass();
|
||||
allowPrivateMethod = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void defineSetting(Class<? extends SettingControl> settingsClass, Method method, PlatformEventType eventType, String settingName) {
|
||||
try {
|
||||
int index = settingInfos.size();
|
||||
SettingInfo si = new SettingInfo(FIELD_SETTING_PREFIX + index, index);
|
||||
si.settingControl = instantiateSettingControl(settingsClass);
|
||||
Control c = si.settingControl;
|
||||
c.setDefault();
|
||||
String defaultValue = c.getValueSafe();
|
||||
if (defaultValue != null) {
|
||||
Type settingType = TypeLibrary.createType(settingsClass);
|
||||
ArrayList<AnnotationElement> aes = new ArrayList<>();
|
||||
for (Annotation a : method.getDeclaredAnnotations()) {
|
||||
AnnotationElement ae = TypeLibrary.createAnnotation(a);
|
||||
if (ae != null) {
|
||||
aes.add(ae);
|
||||
}
|
||||
}
|
||||
aes.trimToSize();
|
||||
eventControls.put(settingName, si.settingControl);
|
||||
eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes));
|
||||
settingInfos.add(si);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
// Programming error by user, fail fast
|
||||
throw new InstantiationError("Could not instantiate setting " + settingsClass.getName() + " for event " + eventType.getLogName() + ". " + e.getMessage());
|
||||
} catch (IllegalAccessException e) {
|
||||
// Programming error by user, fail fast
|
||||
throw new IllegalAccessError("Could not access setting " + settingsClass.getName() + " for event " + eventType.getLogName() + ". " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private SettingControl instantiateSettingControl(Class<? extends SettingControl> settingControlClass) throws IllegalAccessException, InstantiationException {
|
||||
SecuritySupport.makeVisibleToJFR(settingControlClass);
|
||||
final Constructor<?> cc;
|
||||
try {
|
||||
cc = settingControlClass.getDeclaredConstructors()[0];
|
||||
} catch (Exception e) {
|
||||
throw (Error) new InternalError("Could not get constructor for " + settingControlClass.getName()).initCause(e);
|
||||
}
|
||||
SecuritySupport.setAccessible(cc);
|
||||
try {
|
||||
return (SettingControl) cc.newInstance();
|
||||
} catch (IllegalArgumentException | InvocationTargetException e) {
|
||||
throw (Error) new InternalError("Could not instantiate setting for class " + settingControlClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static Control defineEnabled(PlatformEventType type) {
|
||||
Enabled enabled = type.getAnnotation(Enabled.class);
|
||||
// Java events are enabled by default,
|
||||
// JVM events are not, maybe they should be? Would lower learning curve
|
||||
// there too.
|
||||
String def = type.isJVM() ? "false" : "true";
|
||||
if (enabled != null) {
|
||||
def = Boolean.toString(enabled.value());
|
||||
}
|
||||
type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_ENABLED, Enabled.NAME, def, Collections.emptyList()));
|
||||
return new EnabledSetting(type, def);
|
||||
}
|
||||
|
||||
private static Control defineThreshold(PlatformEventType type) {
|
||||
Threshold threshold = type.getAnnotation(Threshold.class);
|
||||
String def = "0 ns";
|
||||
if (threshold != null) {
|
||||
def = threshold.value();
|
||||
}
|
||||
type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THRESHOLD, Threshold.NAME, def, Collections.emptyList()));
|
||||
return new ThresholdSetting(type, def);
|
||||
}
|
||||
|
||||
private static Control defineStackTrace(PlatformEventType type) {
|
||||
StackTrace stackTrace = type.getAnnotation(StackTrace.class);
|
||||
String def = "true";
|
||||
if (stackTrace != null) {
|
||||
def = Boolean.toString(stackTrace.value());
|
||||
}
|
||||
type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_STACK_TRACE, StackTrace.NAME, def, Collections.emptyList()));
|
||||
return new StackTraceSetting(type, def);
|
||||
}
|
||||
|
||||
private static Control defineCutoff(PlatformEventType type) {
|
||||
Cutoff cutoff = type.getAnnotation(Cutoff.class);
|
||||
String def = Cutoff.INIFITY;
|
||||
if (cutoff != null) {
|
||||
def = cutoff.value();
|
||||
}
|
||||
type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_CUTOFF, Cutoff.NAME, def, Collections.emptyList()));
|
||||
return new CutoffSetting(type, def);
|
||||
}
|
||||
|
||||
|
||||
private static Control definePeriod(PlatformEventType type) {
|
||||
Period period = type.getAnnotation(Period.class);
|
||||
String def = "everyChunk";
|
||||
if (period != null) {
|
||||
def = period.value();
|
||||
}
|
||||
type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_PERIOD, PeriodSetting.NAME, def, Collections.emptyList()));
|
||||
return new PeriodSetting(type, def);
|
||||
}
|
||||
|
||||
void disable() {
|
||||
for (Control c : eventControls.values()) {
|
||||
if (c instanceof EnabledSetting) {
|
||||
c.setValueSafe("false");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writeActiveSettingEvent() {
|
||||
if (!type.isRegistered()) {
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, Control> entry : eventControls.entrySet()) {
|
||||
Control c = entry.getValue();
|
||||
if (Utils.isSettingVisible(c, type.hasEventHook())) {
|
||||
String value = c.getLastValue();
|
||||
if (value == null) {
|
||||
value = c.getDefaultValue();
|
||||
}
|
||||
ActiveSettingEvent ase = new ActiveSettingEvent();
|
||||
ase.id = type.getId();
|
||||
ase.name = entry.getKey();
|
||||
ase.value = value;
|
||||
ase.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Entry<String, Control>> getEntries() {
|
||||
return eventControls.entrySet();
|
||||
}
|
||||
|
||||
public PlatformEventType getEventType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getSettingsId() {
|
||||
return idName;
|
||||
}
|
||||
|
||||
public List<SettingInfo> getSettingInfos() {
|
||||
return settingInfos;
|
||||
}
|
||||
}
|
||||
338
jdkSrc/jdk8/jdk/jfr/internal/EventHandlerCreator.java
Normal file
338
jdkSrc/jdk8/jdk/jfr/internal/EventHandlerCreator.java
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.EventInstrumentation.FieldInfo;
|
||||
import jdk.jfr.internal.EventInstrumentation.SettingInfo;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
|
||||
final class EventHandlerCreator {
|
||||
// TODO:
|
||||
// How can we find out class version without loading a
|
||||
// class as resource in a privileged block and use ASM to inspect
|
||||
// the contents. Using '52' even though we know later versions
|
||||
// are available. The reason for this is compatibility aspects
|
||||
// with for example WLS.
|
||||
private static final int CLASS_VERSION = 52;
|
||||
|
||||
// This is needed so a new EventHandler is automatically generated in MetadataRespoistory
|
||||
// if a user Event class is loaded using APPCDS/CDS.
|
||||
private static final String SUFFIX = "_" + System.currentTimeMillis() + "-" + JVM.getJVM().getPid();
|
||||
|
||||
private static final String FIELD_EVENT_TYPE = "platformEventType";
|
||||
private static final String FIELD_PREFIX_STRING_POOL = "stringPool";
|
||||
|
||||
private final static Class<? extends EventHandler> eventHandlerProxy = EventHandlerProxyCreator.proxyClass;
|
||||
private final static Type TYPE_STRING_POOL = Type.getType(StringPool.class);
|
||||
private final static Type TYPE_EVENT_WRITER = Type.getType(EventWriter.class);
|
||||
private final static Type TYPE_PLATFORM_EVENT_TYPE = Type.getType(PlatformEventType.class);
|
||||
private final static Type TYPE_EVENT_HANDLER = Type.getType(eventHandlerProxy);
|
||||
private final static Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
|
||||
private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class);
|
||||
private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class);
|
||||
private final static String DESCRIPTOR_EVENT_HANDLER = "(" + Type.BOOLEAN_TYPE.getDescriptor() + TYPE_EVENT_TYPE.getDescriptor() + TYPE_EVENT_CONTROL.getDescriptor() + ")V";
|
||||
private final static Method METHOD_GET_EVENT_WRITER = new Method("getEventWriter", "()" + TYPE_EVENT_WRITER.getDescriptor());
|
||||
private final static Method METHOD_EVENT_HANDLER_CONSTRUCTOR = new Method("<init>", DESCRIPTOR_EVENT_HANDLER);
|
||||
private final static Method METHOD_RESET = new Method("reset", "()V");
|
||||
|
||||
private final ClassWriter classWriter;
|
||||
private final String className;
|
||||
private final String internalClassName;
|
||||
private final List<SettingInfo> settingInfos;
|
||||
private final List<FieldInfo> fields;
|
||||
|
||||
public EventHandlerCreator(long id, List<SettingInfo> settingInfos, List<FieldInfo> fields) {
|
||||
this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
this.className = makeEventHandlerName(id);
|
||||
this.internalClassName = ASMToolkit.getInternalName(className);
|
||||
this.settingInfos = settingInfos;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public static String makeEventHandlerName(long id) {
|
||||
return eventHandlerProxy.getName() + id + SUFFIX;
|
||||
}
|
||||
|
||||
public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends Event> eventClass) {
|
||||
this(id, settingInfos, createFieldInfos(eventClass, type));
|
||||
}
|
||||
|
||||
private static List<FieldInfo> createFieldInfos(Class<? extends Event> eventClass, EventType type) throws Error {
|
||||
List<FieldInfo> fieldInfos = new ArrayList<>();
|
||||
for (ValueDescriptor v : type.getFields()) {
|
||||
// Only value descriptors that are not fields on the event class.
|
||||
if (v != TypeLibrary.STACK_TRACE_FIELD && v != TypeLibrary.THREAD_FIELD) {
|
||||
String fieldName = PrivateAccess.getInstance().getFieldName(v);
|
||||
String fieldDescriptor = ASMToolkit.getDescriptor(v.getTypeName());
|
||||
Class<?> c = eventClass;
|
||||
String internalName = null;
|
||||
while (c != Event.class) {
|
||||
try {
|
||||
Field field = c.getDeclaredField(fieldName);
|
||||
if (c == eventClass || !Modifier.isPrivate(field.getModifiers())) {
|
||||
internalName = ASMToolkit.getInternalName(c.getName());
|
||||
break;
|
||||
}
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
// ignore
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
if (internalName != null) {
|
||||
fieldInfos.add(new FieldInfo(fieldName, fieldDescriptor, internalName));
|
||||
} else {
|
||||
throw new InternalError("Could not locate field " + fieldName + " for event type" + type.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldInfos;
|
||||
}
|
||||
|
||||
public Class<? extends EventHandler> makeEventHandlerClass() {
|
||||
buildClassInfo();
|
||||
buildConstructor();
|
||||
buildWriteMethod();
|
||||
byte[] bytes = classWriter.toByteArray();
|
||||
ASMToolkit.logASM(className, bytes);
|
||||
return SecuritySupport.defineClass(className, bytes, Event.class.getClassLoader()).asSubclass(EventHandler.class);
|
||||
}
|
||||
|
||||
public static EventHandler instantiateEventHandler(Class<? extends EventHandler> handlerClass, boolean registered, EventType eventType, EventControl eventControl) throws Error {
|
||||
final Constructor<?> cc;
|
||||
try {
|
||||
cc = handlerClass.getDeclaredConstructors()[0];
|
||||
} catch (Exception e) {
|
||||
throw (Error) new InternalError("Could not get handler constructor for " + eventType.getName()).initCause(e);
|
||||
}
|
||||
// Users should not be allowed to create instances of the event handler
|
||||
// so we need to unlock it here.
|
||||
SecuritySupport.setAccessible(cc);
|
||||
try {
|
||||
List<SettingInfo> settingInfos = eventControl.getSettingInfos();
|
||||
Object[] arguments = new Object[3 + settingInfos.size()];
|
||||
arguments[0] = registered;
|
||||
arguments[1] = eventType;
|
||||
arguments[2] = eventControl;
|
||||
for (SettingInfo si : settingInfos) {
|
||||
arguments[si.index + 3] = si.settingControl;
|
||||
}
|
||||
return (EventHandler) cc.newInstance(arguments);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw (Error) new InternalError("Could not instantiate event handler for " + eventType.getName() + ". " + e.getMessage()).initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildConstructor() {
|
||||
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PRIVATE, METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), makeConstructorDescriptor(settingInfos), null, null);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0); // this
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1); // registered
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2); // event type
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 3); // event control
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(eventHandlerProxy), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false);
|
||||
for (SettingInfo si : settingInfos) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0); // this
|
||||
mv.visitVarInsn(Opcodes.ALOAD, si.index + 4); // Setting Control
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
|
||||
}
|
||||
// initialized string field writers
|
||||
int fieldIndex = 0;
|
||||
for (FieldInfo field : fields) {
|
||||
if (field.isString()) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(eventHandlerProxy), "createStringFieldWriter", "()" + TYPE_STRING_POOL.getDescriptor(), false);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
|
||||
}
|
||||
fieldIndex++;
|
||||
}
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void buildClassInfo() {
|
||||
String internalSuperName = ASMToolkit.getInternalName(eventHandlerProxy.getName());
|
||||
classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
|
||||
for (SettingInfo si : settingInfos) {
|
||||
classWriter.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor(), null, null);
|
||||
}
|
||||
int fieldIndex = 0;
|
||||
for (FieldInfo field : fields) {
|
||||
if (field.isString()) {
|
||||
classWriter.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, FIELD_PREFIX_STRING_POOL+ fieldIndex, TYPE_STRING_POOL.getDescriptor(), null, null);
|
||||
}
|
||||
fieldIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void visitMethod(final MethodVisitor mv, final int opcode, final Type type, final Method method) {
|
||||
mv.visitMethodInsn(opcode, type.getInternalName(), method.getName(), method.getDescriptor(), false);
|
||||
}
|
||||
|
||||
private void buildWriteMethod() {
|
||||
int argIndex = 0; // // indexes the argument type array, the argument type array does not include 'this'
|
||||
int slotIndex = 1; // indexes the proper slot in the local variable table, takes type size into account, therefore sometimes argIndex != slotIndex
|
||||
int fieldIndex = 0;
|
||||
Method desc = ASMToolkit.makeWriteMethod(fields);
|
||||
Type[] argumentTypes = Type.getArgumentTypes(desc.getDescriptor());
|
||||
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, desc.getName(), desc.getDescriptor(), null, null);
|
||||
mv.visitCode();
|
||||
Label start = new Label();
|
||||
Label endTryBlock = new Label();
|
||||
Label exceptionHandler = new Label();
|
||||
mv.visitTryCatchBlock(start, endTryBlock, exceptionHandler, "java/lang/Throwable");
|
||||
mv.visitLabel(start);
|
||||
visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
|
||||
// stack: [BW]
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [BW], [BW]
|
||||
// write begin event
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
// stack: [BW], [BW], [this]
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, TYPE_EVENT_HANDLER.getInternalName(), FIELD_EVENT_TYPE, TYPE_PLATFORM_EVENT_TYPE.getDescriptor());
|
||||
// stack: [BW], [BW], [BS]
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.asASM());
|
||||
// stack: [BW], [integer]
|
||||
Label recursive = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFEQ, recursive);
|
||||
// stack: [BW]
|
||||
// write startTime
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [BW], [BW]
|
||||
mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
|
||||
// stack: [BW], [BW], [long]
|
||||
slotIndex += argumentTypes[argIndex++].getSize();
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM());
|
||||
// stack: [BW]
|
||||
fieldIndex++;
|
||||
// write duration
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [BW], [BW]
|
||||
mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
|
||||
// stack: [BW], [BW], [long]
|
||||
slotIndex += argumentTypes[argIndex++].getSize();
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM());
|
||||
// stack: [BW]
|
||||
fieldIndex++;
|
||||
// write eventThread
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [BW], [BW]
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.asASM());
|
||||
// stack: [BW]
|
||||
// write stackTrace
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [BW], [BW]
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.asASM());
|
||||
// stack: [BW]
|
||||
// write custom fields
|
||||
while (fieldIndex < fields.size()) {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [BW], [BW]
|
||||
mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
|
||||
// stack:[BW], [BW], [field]
|
||||
slotIndex += argumentTypes[argIndex++].getSize();
|
||||
FieldInfo field = fields.get(fieldIndex);
|
||||
if (field.isString()) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
// stack:[BW], [BW], [field], [this]
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, this.internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
|
||||
// stack:[BW], [BW], [field], [string]
|
||||
}
|
||||
EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field);
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, eventMethod.asASM());
|
||||
// stack: [BW]
|
||||
fieldIndex++;
|
||||
}
|
||||
// stack: [BW]
|
||||
// write end event (writer already on stack)
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.asASM());
|
||||
// stack [integer]
|
||||
// notified -> restart event write attempt
|
||||
mv.visitJumpInsn(Opcodes.IFEQ, start);
|
||||
// stack:
|
||||
mv.visitLabel(endTryBlock);
|
||||
Label end = new Label();
|
||||
mv.visitJumpInsn(Opcodes.GOTO, end);
|
||||
mv.visitLabel(exceptionHandler);
|
||||
// stack: [ex]
|
||||
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
|
||||
visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
|
||||
// stack: [ex] [BW]
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [ex] [BW] [BW]
|
||||
Label rethrow = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFNULL, rethrow);
|
||||
// stack: [ex] [BW]
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
// stack: [ex] [BW] [BW]
|
||||
visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, METHOD_RESET);
|
||||
mv.visitLabel(rethrow);
|
||||
// stack:[ex] [BW]
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 2, new Object[] {"java/lang/Throwable", TYPE_EVENT_WRITER.getInternalName()});
|
||||
mv.visitInsn(Opcodes.POP);
|
||||
// stack:[ex]
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
mv.visitLabel(recursive);
|
||||
// stack: [BW]
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] { TYPE_EVENT_WRITER.getInternalName()} );
|
||||
mv.visitInsn(Opcodes.POP);
|
||||
mv.visitLabel(end);
|
||||
// stack:
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private static String makeConstructorDescriptor(List<SettingInfo> settingsInfos) {
|
||||
StringJoiner constructordescriptor = new StringJoiner("", "(", ")V");
|
||||
constructordescriptor.add(Type.BOOLEAN_TYPE.getDescriptor());
|
||||
constructordescriptor.add(Type.getType(EventType.class).getDescriptor());
|
||||
constructordescriptor.add(Type.getType(EventControl.class).getDescriptor());
|
||||
for (int i = 0; i < settingsInfos.size(); i++) {
|
||||
constructordescriptor.add(TYPE_SETTING_CONTROL.getDescriptor());
|
||||
}
|
||||
return constructordescriptor.toString();
|
||||
}
|
||||
}
|
||||
126
jdkSrc/jdk8/jdk/jfr/internal/EventHandlerProxyCreator.java
Normal file
126
jdkSrc/jdk8/jdk/jfr/internal/EventHandlerProxyCreator.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, Red Hat, Inc.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
|
||||
/*
|
||||
* Generates an EventHandler subclass dynamically, named EventHandlerProxy.
|
||||
* EventHandlerProxy is located in a publicly accessible package (jdk.jfr)
|
||||
* and is used as a superclass for classes generated by EventHandlerCreator.
|
||||
* The rationale behind this scheme is to block access to jdk.jfr.internal
|
||||
* package and sub-packages when there is a SecurityManager installed, while
|
||||
* allowing application-defined event classes to invoke the required internal
|
||||
* APIs.
|
||||
*/
|
||||
final class EventHandlerProxyCreator {
|
||||
private static final int CLASS_VERSION = 52;
|
||||
|
||||
private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class);
|
||||
private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class);
|
||||
private final static String DESCRIPTOR_EVENT_HANDLER = "(" + Type.BOOLEAN_TYPE.getDescriptor() + TYPE_EVENT_TYPE.getDescriptor() + TYPE_EVENT_CONTROL.getDescriptor() + ")V";
|
||||
private final static Method METHOD_EVENT_HANDLER_CONSTRUCTOR = new Method("<init>", DESCRIPTOR_EVENT_HANDLER);
|
||||
private final static String DESCRIPTOR_TIME_STAMP = "()" + Type.LONG_TYPE.getDescriptor();
|
||||
private final static Method METHOD_TIME_STAMP = new Method("timestamp", DESCRIPTOR_TIME_STAMP);
|
||||
private final static String DESCRIPTOR_DURATION = "(" + Type.LONG_TYPE.getDescriptor() + ")" + Type.LONG_TYPE.getDescriptor();
|
||||
private final static Method METHOD_DURATION = new Method("duration", DESCRIPTOR_DURATION);
|
||||
|
||||
private final static ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
private final static String className = "jdk.jfr.proxy.internal.EventHandlerProxy";
|
||||
private final static String internalClassName = ASMToolkit.getInternalName(className);
|
||||
|
||||
// Create the Proxy class instance after all the previous static fields were initialized (textual order)
|
||||
static final Class<? extends EventHandler> proxyClass = EventHandlerProxyCreator.makeEventHandlerProxyClass();
|
||||
|
||||
static void ensureInitialized() {
|
||||
// trigger clinit which will setup the EventHandlerProxy class.
|
||||
}
|
||||
|
||||
public static Class<? extends EventHandler> makeEventHandlerProxyClass() {
|
||||
buildClassInfo();
|
||||
buildConstructor();
|
||||
buildTimestampMethod();
|
||||
buildDurationMethod();
|
||||
byte[] bytes = classWriter.toByteArray();
|
||||
ASMToolkit.logASM(className, bytes);
|
||||
return SecuritySupport.defineClass(className, bytes, Event.class.getClassLoader()).asSubclass(EventHandler.class);
|
||||
}
|
||||
|
||||
private static void buildConstructor() {
|
||||
MethodVisitor mv = classWriter.visitMethod(0x0, METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), makeConstructorDescriptor(), null, null);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0); // this
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1); // registered
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2); // event type
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 3); // event control
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(EventHandler.class), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false);
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private static void buildClassInfo() {
|
||||
String internalSuperName = ASMToolkit.getInternalName(EventHandler.class.getName());
|
||||
classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
|
||||
}
|
||||
|
||||
private static void buildTimestampMethod() {
|
||||
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), null, null);
|
||||
mv.visitCode();
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(EventHandler.class), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
|
||||
mv.visitInsn(Opcodes.LRETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private static void buildDurationMethod() {
|
||||
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, METHOD_DURATION.getName(), METHOD_DURATION.getDescriptor(), null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.LLOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(EventHandler.class), METHOD_DURATION.getName(), METHOD_DURATION.getDescriptor(), false);
|
||||
mv.visitInsn(Opcodes.LRETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private static String makeConstructorDescriptor() {
|
||||
StringJoiner constructordescriptor = new StringJoiner("", "(", ")V");
|
||||
constructordescriptor.add(Type.BOOLEAN_TYPE.getDescriptor());
|
||||
constructordescriptor.add(Type.getType(EventType.class).getDescriptor());
|
||||
constructordescriptor.add(Type.getType(EventControl.class).getDescriptor());
|
||||
return constructordescriptor.toString();
|
||||
}
|
||||
}
|
||||
528
jdkSrc/jdk8/jdk/jfr/internal/EventInstrumentation.java
Normal file
528
jdkSrc/jdk8/jdk/jfr/internal/EventInstrumentation.java
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
||||
import jdk.internal.org.objectweb.asm.tree.AnnotationNode;
|
||||
import jdk.internal.org.objectweb.asm.tree.ClassNode;
|
||||
import jdk.internal.org.objectweb.asm.tree.FieldNode;
|
||||
import jdk.internal.org.objectweb.asm.tree.MethodNode;
|
||||
import jdk.jfr.Enabled;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.Registered;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.SettingDefinition;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
|
||||
/**
|
||||
* Class responsible for adding instrumentation to a subclass of {@link Event}.
|
||||
*
|
||||
*/
|
||||
public final class EventInstrumentation {
|
||||
static final class SettingInfo {
|
||||
private String methodName;
|
||||
private String internalSettingName;
|
||||
private String settingDescriptor;
|
||||
final String fieldName;
|
||||
final int index;
|
||||
// Used when instantiating Setting
|
||||
SettingControl settingControl;
|
||||
|
||||
public SettingInfo(String fieldName, int index) {
|
||||
this.fieldName = fieldName;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
static final class FieldInfo {
|
||||
private final static Type STRING = Type.getType(String.class);
|
||||
final String fieldName;
|
||||
final String fieldDescriptor;
|
||||
final String internalClassName;
|
||||
|
||||
public FieldInfo(String fieldName, String fieldDescriptor, String internalClassName) {
|
||||
this.fieldName = fieldName;
|
||||
this.fieldDescriptor = fieldDescriptor;
|
||||
this.internalClassName = internalClassName;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return STRING.getDescriptor().equals(fieldDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String FIELD_EVENT_THREAD = "eventThread";
|
||||
public static final String FIELD_STACK_TRACE = "stackTrace";
|
||||
public static final String FIELD_DURATION = "duration";
|
||||
|
||||
static final String FIELD_EVENT_HANDLER = "eventHandler";
|
||||
static final String FIELD_START_TIME = "startTime";
|
||||
|
||||
private static final Class<? extends EventHandler> eventHandlerProxy = EventHandlerProxyCreator.proxyClass;
|
||||
private static final Type ANNOTATION_TYPE_NAME = Type.getType(Name.class);
|
||||
private static final Type ANNOTATION_TYPE_REGISTERED = Type.getType(Registered.class);
|
||||
private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class);
|
||||
private static final Type TYPE_EVENT_HANDLER = Type.getType(eventHandlerProxy);
|
||||
private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
|
||||
private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]);
|
||||
private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]);
|
||||
private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]);
|
||||
private static final Method METHOD_IS_ENABLED = new Method("isEnabled", Type.BOOLEAN_TYPE, new Type[0]);
|
||||
private static final Method METHOD_TIME_STAMP = new Method("timestamp", Type.LONG_TYPE, new Type[0]);
|
||||
private static final Method METHOD_EVENT_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[0]);
|
||||
private static final Method METHOD_EVENT_HANDLER_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[] { Type.LONG_TYPE });
|
||||
private static final Method METHOD_DURATION = new Method("duration", Type.LONG_TYPE, new Type[] { Type.LONG_TYPE });
|
||||
|
||||
private final ClassNode classNode;
|
||||
private final List<SettingInfo> settingInfos;
|
||||
private final List<FieldInfo> fieldInfos;;
|
||||
private final Method writeMethod;
|
||||
private final String eventHandlerXInternalName;
|
||||
private final String eventName;
|
||||
private boolean guardHandlerReference;
|
||||
private Class<?> superClass;
|
||||
|
||||
EventInstrumentation(Class<?> superClass, byte[] bytes, long id) {
|
||||
this.superClass = superClass;
|
||||
this.classNode = createClassNode(bytes);
|
||||
this.settingInfos = buildSettingInfos(superClass, classNode);
|
||||
this.fieldInfos = buildFieldInfos(superClass, classNode);
|
||||
this.writeMethod = makeWriteMethod(fieldInfos);
|
||||
this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id));
|
||||
String n = annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class);
|
||||
this.eventName = n == null ? classNode.name.replace("/", ".") : n;
|
||||
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return classNode.name.replace("/",".");
|
||||
}
|
||||
|
||||
private ClassNode createClassNode(byte[] bytes) {
|
||||
ClassNode classNode = new ClassNode();
|
||||
ClassReader classReader = new ClassReader(bytes);
|
||||
classReader.accept(classNode, 0);
|
||||
return classNode;
|
||||
}
|
||||
|
||||
boolean isRegistered() {
|
||||
Boolean result = annotationValue(classNode, ANNOTATION_TYPE_REGISTERED.getDescriptor(), Boolean.class);
|
||||
if (result != null) {
|
||||
return result.booleanValue();
|
||||
}
|
||||
if (superClass != null) {
|
||||
Registered r = superClass.getAnnotation(Registered.class);
|
||||
if (r != null) {
|
||||
return r.value();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isEnabled() {
|
||||
Boolean result = annotationValue(classNode, ANNOTATION_TYPE_ENABLED.getDescriptor(), Boolean.class);
|
||||
if (result != null) {
|
||||
return result.booleanValue();
|
||||
}
|
||||
if (superClass != null) {
|
||||
Enabled e = superClass.getAnnotation(Enabled.class);
|
||||
if (e != null) {
|
||||
return e.value();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T annotationValue(ClassNode classNode, String typeDescriptor, Class<?> type) {
|
||||
if (classNode.visibleAnnotations != null) {
|
||||
for (AnnotationNode a : classNode.visibleAnnotations) {
|
||||
if (typeDescriptor.equals(a.desc)) {
|
||||
List<Object> values = a.values;
|
||||
if (values != null && values.size() == 2) {
|
||||
Object key = values.get(0);
|
||||
Object value = values.get(1);
|
||||
if (key instanceof String && value != null) {
|
||||
if (type == value.getClass()) {
|
||||
String keyName = (String) key;
|
||||
if ("value".equals(keyName)) {
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<SettingInfo> buildSettingInfos(Class<?> superClass, ClassNode classNode) {
|
||||
Set<String> methodSet = new HashSet<>();
|
||||
List<SettingInfo> settingInfos = new ArrayList<>();
|
||||
String settingDescriptor = Type.getType(SettingDefinition.class).getDescriptor();
|
||||
for (MethodNode m : classNode.methods) {
|
||||
if (m.visibleAnnotations != null) {
|
||||
for (AnnotationNode an : m.visibleAnnotations) {
|
||||
// We can't really validate the method at this
|
||||
// stage. We would need to check that the parameter
|
||||
// is an instance of SettingControl.
|
||||
if (settingDescriptor.equals(an.desc)) {
|
||||
Type returnType = Type.getReturnType(m.desc);
|
||||
if (returnType.equals(Type.getType(Boolean.TYPE))) {
|
||||
Type[] args = Type.getArgumentTypes(m.desc);
|
||||
if (args.length == 1) {
|
||||
Type paramType = args[0];
|
||||
String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size();
|
||||
int index = settingInfos.size();
|
||||
SettingInfo si = new SettingInfo(fieldName, index);
|
||||
si.methodName = m.name;
|
||||
si.settingDescriptor = paramType.getDescriptor();
|
||||
si.internalSettingName = paramType.getInternalName();
|
||||
methodSet.add(m.name);
|
||||
settingInfos.add(si);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) {
|
||||
for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
|
||||
if (!methodSet.contains(method.getName())) {
|
||||
// skip private method in base classes
|
||||
if (!Modifier.isPrivate(method.getModifiers())) {
|
||||
if (method.getReturnType().equals(Boolean.TYPE)) {
|
||||
if (method.getParameterCount() == 1) {
|
||||
Parameter param = method.getParameters()[0];
|
||||
Type paramType = Type.getType(param.getType());
|
||||
String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size();
|
||||
int index = settingInfos.size();
|
||||
SettingInfo si = new SettingInfo(fieldName, index);
|
||||
si.methodName = method.getName();
|
||||
si.settingDescriptor = paramType.getDescriptor();
|
||||
si.internalSettingName = paramType.getInternalName();
|
||||
methodSet.add(method.getName());
|
||||
settingInfos.add(si);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return settingInfos;
|
||||
|
||||
}
|
||||
|
||||
private static List<FieldInfo> buildFieldInfos(Class<?> superClass, ClassNode classNode) {
|
||||
Set<String> fieldSet = new HashSet<>();
|
||||
List<FieldInfo> fieldInfos = new ArrayList<>(classNode.fields.size());
|
||||
// These two field are added by native as transient so they will be
|
||||
// ignored by the loop below.
|
||||
// The benefit of adding them manually is that we can
|
||||
// control in which order they occur and we can add @Name, @Description
|
||||
// in Java, instead of in native. It also means code for adding implicit
|
||||
// fields for native can be reused by Java.
|
||||
fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name));
|
||||
fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name));
|
||||
for (FieldNode field : classNode.fields) {
|
||||
String className = Type.getType(field.desc).getClassName();
|
||||
if (!fieldSet.contains(field.name) && isValidField(field.access, className)) {
|
||||
FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name);
|
||||
fieldInfos.add(fi);
|
||||
fieldSet.add(field.name);
|
||||
}
|
||||
}
|
||||
for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) {
|
||||
for (Field field : c.getDeclaredFields()) {
|
||||
// skip private field in base classes
|
||||
if (!Modifier.isPrivate(field.getModifiers())) {
|
||||
if (isValidField(field.getModifiers(), field.getType().getName())) {
|
||||
String fieldName = field.getName();
|
||||
if (!fieldSet.contains(fieldName)) {
|
||||
Type fieldType = Type.getType(field.getType());
|
||||
String internalClassName = ASMToolkit.getInternalName(c.getName());
|
||||
fieldInfos.add(new FieldInfo(fieldName, fieldType.getDescriptor(), internalClassName));
|
||||
fieldSet.add(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldInfos;
|
||||
}
|
||||
|
||||
public static boolean isValidField(int access, String className) {
|
||||
if (Modifier.isTransient(access) || Modifier.isStatic(access)) {
|
||||
return false;
|
||||
}
|
||||
return jdk.jfr.internal.Type.isValidJavaFieldType(className);
|
||||
}
|
||||
|
||||
public byte[] buildInstrumented() {
|
||||
makeInstrumented();
|
||||
return toByteArray();
|
||||
}
|
||||
|
||||
private byte[] toByteArray() {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
classNode.accept(cw);
|
||||
cw.visitEnd();
|
||||
byte[] result = cw.toByteArray();
|
||||
Utils.writeGeneratedASM(classNode.name, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] builUninstrumented() {
|
||||
makeUninstrumented();
|
||||
return toByteArray();
|
||||
}
|
||||
|
||||
private void makeInstrumented() {
|
||||
// MyEvent#isEnabled()
|
||||
updateMethod(METHOD_IS_ENABLED, methodVisitor -> {
|
||||
Label nullLabel = new Label();
|
||||
if (guardHandlerReference) {
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel);
|
||||
}
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
|
||||
ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED);
|
||||
methodVisitor.visitInsn(Opcodes.IRETURN);
|
||||
if (guardHandlerReference) {
|
||||
methodVisitor.visitLabel(nullLabel);
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
methodVisitor.visitInsn(Opcodes.ICONST_0);
|
||||
methodVisitor.visitInsn(Opcodes.IRETURN);
|
||||
}
|
||||
});
|
||||
|
||||
// MyEvent#begin()
|
||||
updateMethod(METHOD_BEGIN, methodVisitor -> {
|
||||
methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
|
||||
ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP);
|
||||
methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
|
||||
methodVisitor.visitInsn(Opcodes.RETURN);
|
||||
});
|
||||
|
||||
// MyEvent#end()
|
||||
updateMethod(METHOD_END, methodVisitor -> {
|
||||
methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
|
||||
ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_DURATION);
|
||||
methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
|
||||
methodVisitor.visitInsn(Opcodes.RETURN);
|
||||
methodVisitor.visitMaxs(0, 0);
|
||||
});
|
||||
|
||||
// MyEvent#commit() - Java event writer
|
||||
updateMethod(METHOD_COMMIT, methodVisitor -> {
|
||||
// if (!isEnable()) {
|
||||
// return;
|
||||
// }
|
||||
methodVisitor.visitCode();
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
|
||||
Label l0 = new Label();
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
|
||||
methodVisitor.visitInsn(Opcodes.RETURN);
|
||||
methodVisitor.visitLabel(l0);
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
// if (startTime == 0) {
|
||||
// startTime = EventWriter.timestamp();
|
||||
// } else {
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
|
||||
methodVisitor.visitInsn(Opcodes.LCONST_0);
|
||||
methodVisitor.visitInsn(Opcodes.LCMP);
|
||||
Label durationalEvent = new Label();
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(),
|
||||
METHOD_TIME_STAMP.getDescriptor(), false);
|
||||
methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
|
||||
Label commit = new Label();
|
||||
methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
|
||||
// if (duration == 0) {
|
||||
// duration = EventWriter.timestamp() - startTime;
|
||||
// }
|
||||
// }
|
||||
methodVisitor.visitLabel(durationalEvent);
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
|
||||
methodVisitor.visitInsn(Opcodes.LCONST_0);
|
||||
methodVisitor.visitInsn(Opcodes.LCMP);
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
|
||||
methodVisitor.visitInsn(Opcodes.LSUB);
|
||||
methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
|
||||
methodVisitor.visitLabel(commit);
|
||||
// if (shouldCommit()) {
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
|
||||
Label end = new Label();
|
||||
// eventHandler.write(...);
|
||||
// }
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
|
||||
|
||||
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
|
||||
for (FieldInfo fi : fieldInfos) {
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
|
||||
}
|
||||
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
|
||||
methodVisitor.visitLabel(end);
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
methodVisitor.visitInsn(Opcodes.RETURN);
|
||||
methodVisitor.visitEnd();
|
||||
});
|
||||
|
||||
// MyEvent#shouldCommit()
|
||||
updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
|
||||
Label fail = new Label();
|
||||
// if (!eventHandler.shoouldCommit(duration) goto fail;
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
|
||||
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
|
||||
ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT);
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail);
|
||||
for (SettingInfo si : settingInfos) {
|
||||
// if (!settingsMethod(eventHandler.settingX)) goto fail;
|
||||
methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
|
||||
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
|
||||
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
|
||||
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName);
|
||||
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), si.methodName, "(" + si.settingDescriptor + ")Z", false);
|
||||
methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail);
|
||||
}
|
||||
// return true
|
||||
methodVisitor.visitInsn(Opcodes.ICONST_1);
|
||||
methodVisitor.visitInsn(Opcodes.IRETURN);
|
||||
// return false
|
||||
methodVisitor.visitLabel(fail);
|
||||
methodVisitor.visitInsn(Opcodes.ICONST_0);
|
||||
methodVisitor.visitInsn(Opcodes.IRETURN);
|
||||
});
|
||||
}
|
||||
|
||||
private void makeUninstrumented() {
|
||||
updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT);
|
||||
updateExistingWithReturnFalse(METHOD_IS_ENABLED);
|
||||
updateExistingWithEmptyVoidMethod(METHOD_COMMIT);
|
||||
updateExistingWithEmptyVoidMethod(METHOD_BEGIN);
|
||||
updateExistingWithEmptyVoidMethod(METHOD_END);
|
||||
}
|
||||
|
||||
private final void updateExistingWithEmptyVoidMethod(Method voidMethod) {
|
||||
updateMethod(voidMethod, methodVisitor -> {
|
||||
methodVisitor.visitInsn(Opcodes.RETURN);
|
||||
});
|
||||
}
|
||||
|
||||
private final void updateExistingWithReturnFalse(Method voidMethod) {
|
||||
updateMethod(voidMethod, methodVisitor -> {
|
||||
methodVisitor.visitInsn(Opcodes.ICONST_0);
|
||||
methodVisitor.visitInsn(Opcodes.IRETURN);
|
||||
});
|
||||
}
|
||||
|
||||
private MethodNode getMethodNode(Method method) {
|
||||
for (MethodNode m : classNode.methods) {
|
||||
if (m.name.equals(method.getName()) && m.desc.equals(method.getDescriptor())) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final void updateMethod(Method method, Consumer<MethodVisitor> code) {
|
||||
MethodNode old = getMethodNode(method);
|
||||
int index = classNode.methods.indexOf(old);
|
||||
classNode.methods.remove(old);
|
||||
MethodVisitor mv = classNode.visitMethod(old.access, old.name, old.desc, null, null);
|
||||
mv.visitCode();
|
||||
code.accept(mv);
|
||||
mv.visitMaxs(0, 0);
|
||||
MethodNode newMethod = getMethodNode(method);
|
||||
classNode.methods.remove(newMethod);
|
||||
classNode.methods.add(index, newMethod);
|
||||
}
|
||||
|
||||
public static Method makeWriteMethod(List<FieldInfo> fields) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (FieldInfo v : fields) {
|
||||
sb.append(v.fieldDescriptor);
|
||||
}
|
||||
sb.append(")V");
|
||||
return new Method("write", sb.toString());
|
||||
}
|
||||
|
||||
private String getInternalClassName() {
|
||||
return classNode.name;
|
||||
}
|
||||
|
||||
public List<SettingInfo> getSettingInfos() {
|
||||
return settingInfos;
|
||||
}
|
||||
|
||||
public List<FieldInfo> getFieldInfos() {
|
||||
return fieldInfos;
|
||||
}
|
||||
|
||||
public String getEventName() {
|
||||
return eventName;
|
||||
}
|
||||
|
||||
public void setGuardHandler(boolean guardHandlerReference) {
|
||||
this.guardHandlerReference = guardHandlerReference;
|
||||
}
|
||||
}
|
||||
355
jdkSrc/jdk8/jdk/jfr/internal/EventWriter.java
Normal file
355
jdkSrc/jdk8/jdk/jfr/internal/EventWriter.java
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
|
||||
/**
|
||||
* Class must reside in a package with package restriction.
|
||||
*
|
||||
* Users should not have direct access to underlying memory.
|
||||
*
|
||||
*/
|
||||
public final class EventWriter {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
private final static JVM jvm = JVM.getJVM();
|
||||
|
||||
private long startPosition;
|
||||
private long startPositionAddress;
|
||||
private long currentPosition;
|
||||
private long maxPosition;
|
||||
private final long threadID;
|
||||
private PlatformEventType eventType;
|
||||
private int maxEventSize;
|
||||
private boolean started;
|
||||
private boolean valid;
|
||||
private boolean flushOnEnd;
|
||||
// set by the JVM, not private to avoid being optimized out
|
||||
boolean notified;
|
||||
|
||||
public static EventWriter getEventWriter() {
|
||||
EventWriter ew = (EventWriter)JVM.getEventWriter();
|
||||
return ew != null ? ew : JVM.newEventWriter();
|
||||
}
|
||||
|
||||
public void putBoolean(boolean i) {
|
||||
if (isValidForSize(Byte.BYTES)) {
|
||||
currentPosition += Bits.putBoolean(currentPosition, i);
|
||||
}
|
||||
}
|
||||
|
||||
public void putByte(byte i) {
|
||||
if (isValidForSize(Byte.BYTES)) {
|
||||
unsafe.putByte(currentPosition, i);
|
||||
++currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public void putChar(char v) {
|
||||
if (isValidForSize(Character.BYTES + 1)) {
|
||||
putUncheckedLong(v);
|
||||
}
|
||||
}
|
||||
|
||||
private void putUncheckedChar(char v) {
|
||||
putUncheckedLong(v);
|
||||
}
|
||||
|
||||
public void putShort(short v) {
|
||||
if (isValidForSize(Short.BYTES + 1)) {
|
||||
putUncheckedLong(v & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
public void putInt(int v) {
|
||||
if (isValidForSize(Integer.BYTES + 1)) {
|
||||
putUncheckedLong(v & 0x00000000ffffffffL);
|
||||
}
|
||||
}
|
||||
|
||||
private void putUncheckedInt(int v) {
|
||||
putUncheckedLong(v & 0x00000000ffffffffL);
|
||||
}
|
||||
|
||||
public void putFloat(float i) {
|
||||
if (isValidForSize(Float.BYTES)) {
|
||||
currentPosition += Bits.putFloat(currentPosition, i);
|
||||
}
|
||||
}
|
||||
|
||||
public void putLong(long v) {
|
||||
if (isValidForSize(Long.BYTES + 1)) {
|
||||
putUncheckedLong(v);
|
||||
}
|
||||
}
|
||||
|
||||
public void putDouble(double i) {
|
||||
if (isValidForSize(Double.BYTES)) {
|
||||
currentPosition += Bits.putDouble(currentPosition, i);
|
||||
}
|
||||
}
|
||||
|
||||
public void putString(String s, StringPool pool) {
|
||||
if (s == null) {
|
||||
putByte(RecordingInput.STRING_ENCODING_NULL);
|
||||
return;
|
||||
}
|
||||
int length = s.length();
|
||||
if (length == 0) {
|
||||
putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING);
|
||||
return;
|
||||
}
|
||||
if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
|
||||
long l = StringPool.addString(s);
|
||||
if (l > 0) {
|
||||
putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL);
|
||||
putLong(l);
|
||||
return;
|
||||
}
|
||||
}
|
||||
putStringValue(s);
|
||||
return;
|
||||
}
|
||||
|
||||
private void putStringValue(String s) {
|
||||
int length = s.length();
|
||||
if (isValidForSize(1 + 5 + 3 * length)) {
|
||||
putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte
|
||||
putUncheckedInt(length); // max 5 bytes
|
||||
for (int i = 0; i < length; i++) {
|
||||
putUncheckedChar(s.charAt(i)); // max 3 bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void putEventThread() {
|
||||
putLong(threadID);
|
||||
}
|
||||
|
||||
public void putThread(Thread athread) {
|
||||
if (athread == null) {
|
||||
putLong(0L);
|
||||
} else {
|
||||
putLong(jvm.getThreadId(athread));
|
||||
}
|
||||
}
|
||||
|
||||
public void putClass(Class<?> aClass) {
|
||||
if (aClass == null) {
|
||||
putLong(0L);
|
||||
} else {
|
||||
putLong(JVM.getClassIdNonIntrinsic(aClass));
|
||||
}
|
||||
}
|
||||
|
||||
public void putStackTrace() {
|
||||
if (eventType.getStackTraceEnabled()) {
|
||||
putLong(jvm.getStackTraceId(eventType.getStackTraceOffset()));
|
||||
} else {
|
||||
putLong(0L);
|
||||
}
|
||||
}
|
||||
|
||||
private void reserveEventSizeField() {
|
||||
// move currentPosition Integer.Bytes offset from start position
|
||||
if (isValidForSize(Integer.BYTES)) {
|
||||
currentPosition += Integer.BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
currentPosition = startPosition;
|
||||
if (flushOnEnd) {
|
||||
flushOnEnd = flush();
|
||||
}
|
||||
valid = true;
|
||||
started = false;
|
||||
}
|
||||
|
||||
private boolean isValidForSize(int requestedSize) {
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
if (currentPosition + requestedSize > maxPosition) {
|
||||
flushOnEnd = flush(usedSize(), requestedSize);
|
||||
// retry
|
||||
if (currentPosition + requestedSize > maxPosition) {
|
||||
Logger.log(LogTag.JFR_SYSTEM,
|
||||
LogLevel.WARN, () ->
|
||||
"Unable to commit. Requested size " + requestedSize + " too large");
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isNotified() {
|
||||
return notified;
|
||||
}
|
||||
|
||||
private void resetNotified() {
|
||||
notified = false;
|
||||
}
|
||||
|
||||
private int usedSize() {
|
||||
return (int) (currentPosition - startPosition);
|
||||
}
|
||||
|
||||
private boolean flush() {
|
||||
return flush(usedSize(), 0);
|
||||
}
|
||||
|
||||
private boolean flush(int usedSize, int requestedSize) {
|
||||
return JVM.flush(this, usedSize, requestedSize);
|
||||
}
|
||||
|
||||
public boolean beginEvent(PlatformEventType eventType) {
|
||||
if (started) {
|
||||
// recursive write attempt
|
||||
return false;
|
||||
}
|
||||
started = true;
|
||||
this.eventType = eventType;
|
||||
reserveEventSizeField();
|
||||
putLong(eventType.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean endEvent() {
|
||||
if (!valid) {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
final int eventSize = usedSize();
|
||||
if (eventSize > maxEventSize) {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
Bits.putInt(startPosition, makePaddedInt(eventSize));
|
||||
if (isNotified()) {
|
||||
resetNotified();
|
||||
reset();
|
||||
// returning false will trigger restart of the event write attempt
|
||||
return false;
|
||||
}
|
||||
startPosition = currentPosition;
|
||||
unsafe.putAddress(startPositionAddress, startPosition);
|
||||
// the event is now committed
|
||||
if (flushOnEnd) {
|
||||
flushOnEnd = flush();
|
||||
}
|
||||
started = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private EventWriter(long startPos, long maxPos, long startPosAddress, long threadID, boolean valid) {
|
||||
startPosition = currentPosition = startPos;
|
||||
maxPosition = maxPos;
|
||||
startPositionAddress = startPosAddress;
|
||||
this.threadID = threadID;
|
||||
started = false;
|
||||
flushOnEnd = false;
|
||||
this.valid = valid;
|
||||
notified = false;
|
||||
// event may not exceed size for a padded integer
|
||||
maxEventSize = (1 << 28) -1;
|
||||
}
|
||||
|
||||
private static int makePaddedInt(int v) {
|
||||
// bit 0-6 + pad => bit 24 - 31
|
||||
long b1 = (((v >>> 0) & 0x7F) | 0x80) << 24;
|
||||
|
||||
// bit 7-13 + pad => bit 16 - 23
|
||||
long b2 = (((v >>> 7) & 0x7F) | 0x80) << 16;
|
||||
|
||||
// bit 14-20 + pad => bit 8 - 15
|
||||
long b3 = (((v >>> 14) & 0x7F) | 0x80) << 8;
|
||||
|
||||
// bit 21-28 => bit 0 - 7
|
||||
long b4 = (((v >>> 21) & 0x7F)) << 0;
|
||||
|
||||
return (int) (b1 + b2 + b3 + b4);
|
||||
}
|
||||
|
||||
private void putUncheckedLong(long v) {
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 0-6
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 0-6
|
||||
v >>>= 7;
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 7-13
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 7-13
|
||||
v >>>= 7;
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 14-20
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 14-20
|
||||
v >>>= 7;
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 21-27
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 21-27
|
||||
v >>>= 7;
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 28-34
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 28-34
|
||||
v >>>= 7;
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 35-41
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 35-41
|
||||
v >>>= 7;
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 42-48
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 42-48
|
||||
v >>>= 7;
|
||||
|
||||
if ((v & ~0x7FL) == 0L) {
|
||||
putUncheckedByte((byte) v); // 49-55
|
||||
return;
|
||||
}
|
||||
putUncheckedByte((byte) (v | 0x80L)); // 49-55
|
||||
putUncheckedByte((byte) (v >>> 7)); // 56-63, last byte as is.
|
||||
}
|
||||
|
||||
private void putUncheckedByte(byte i) {
|
||||
unsafe.putByte(currentPosition, i);
|
||||
++currentPosition;
|
||||
}
|
||||
}
|
||||
81
jdkSrc/jdk8/jdk/jfr/internal/EventWriterMethod.java
Normal file
81
jdkSrc/jdk8/jdk/jfr/internal/EventWriterMethod.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.commons.Method;
|
||||
import jdk.jfr.internal.EventInstrumentation.FieldInfo;
|
||||
|
||||
public enum EventWriterMethod {
|
||||
|
||||
BEGIN_EVENT("(" + jdk.internal.org.objectweb.asm.Type.getType(PlatformEventType.class).getDescriptor() + ")Z", "???", "beginEvent"),
|
||||
END_EVENT("()Z", "???", "endEvent"),
|
||||
PUT_BYTE("(B)V", "byte", "putByte"),
|
||||
PUT_SHORT("(S)V", "short", "putShort"),
|
||||
PUT_INT("(I)V", "int", "putInt"),
|
||||
PUT_LONG("(J)V", "long", "putLong"),
|
||||
PUT_FLOAT("(F)V", "float", "putFloat"),
|
||||
PUT_DOUBLE("(D)V", "double", "putDouble"),
|
||||
PUT_CHAR("(C)V", "char", "putChar"),
|
||||
PUT_BOOLEAN("(Z)V", "boolean", "putBoolean"),
|
||||
PUT_THREAD("(Ljava/lang/Thread;)V", Type.THREAD.getName(), "putThread"),
|
||||
PUT_CLASS("(Ljava/lang/Class;)V", Type.CLASS.getName(), "putClass"),
|
||||
PUT_STRING("(Ljava/lang/String;Ljdk/jfr/internal/StringPool;)V", Type.STRING.getName(), "putString"),
|
||||
PUT_EVENT_THREAD("()V", Type.THREAD.getName(), "putEventThread"),
|
||||
PUT_STACK_TRACE("()V", Type.TYPES_PREFIX + "StackTrace", "putStackTrace");
|
||||
|
||||
private final Method asmMethod;
|
||||
private final String typeDescriptor;
|
||||
|
||||
EventWriterMethod(String paramSignature, String typeName, String methodName) {
|
||||
this.typeDescriptor = ASMToolkit.getDescriptor(typeName);
|
||||
this.asmMethod = new Method(methodName, paramSignature);
|
||||
}
|
||||
|
||||
public Method asASM() {
|
||||
return asmMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return method in {@link EventWriter} class to use when writing event of
|
||||
* a certain type.
|
||||
*
|
||||
* @param v field info
|
||||
*
|
||||
* @return the method
|
||||
*/
|
||||
public static EventWriterMethod lookupMethod(FieldInfo v) {
|
||||
// event thread
|
||||
if (v.fieldName.equals(EventInstrumentation.FIELD_EVENT_THREAD)) {
|
||||
return EventWriterMethod.PUT_EVENT_THREAD;
|
||||
}
|
||||
for (EventWriterMethod m : EventWriterMethod.values()) {
|
||||
if (v.fieldDescriptor.equals(m.typeDescriptor)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
throw new Error("Unknown type " + v.fieldDescriptor);
|
||||
}
|
||||
}
|
||||
535
jdkSrc/jdk8/jdk/jfr/internal/JVM.java
Normal file
535
jdkSrc/jdk8/jdk/jfr/internal/JVM.java
Normal file
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.Event;
|
||||
|
||||
/**
|
||||
* Interface against the JVM.
|
||||
*
|
||||
*/
|
||||
public final class JVM {
|
||||
private static final JVM jvm = new JVM();
|
||||
|
||||
// JVM signals file changes by doing Object#notifu on this object
|
||||
static final Object FILE_DELTA_CHANGE = new Object();
|
||||
|
||||
static final long RESERVED_CLASS_ID_LIMIT = 400;
|
||||
|
||||
private volatile boolean recording;
|
||||
private volatile boolean nativeOK;
|
||||
|
||||
private static native void registerNatives();
|
||||
|
||||
static {
|
||||
registerNatives();
|
||||
// XXX
|
||||
// for (LogTag tag : LogTag.values()) {
|
||||
// subscribeLogLevel(tag, tag.id);
|
||||
// }
|
||||
Options.ensureInitialized();
|
||||
EventHandlerProxyCreator.ensureInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the one and only JVM.
|
||||
*
|
||||
* @return the JVM
|
||||
*/
|
||||
public static JVM getJVM() {
|
||||
return jvm;
|
||||
}
|
||||
|
||||
private JVM() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin recording events
|
||||
*
|
||||
* Requires that JFR has been started with {@link #createNativeJFR()}
|
||||
*/
|
||||
public native void beginRecording();
|
||||
|
||||
/**
|
||||
* Return ticks
|
||||
*
|
||||
* @return the time, in ticks
|
||||
*
|
||||
*/
|
||||
// @HotSpotIntrinsicCandidate
|
||||
public static native long counterTime();
|
||||
|
||||
|
||||
/**
|
||||
* Emits native periodic event.
|
||||
*
|
||||
* @param eventTypeId type id
|
||||
*
|
||||
* @param timestamp commit time for event
|
||||
* @param when when it is being done {@link Periodic.When}
|
||||
*
|
||||
* @return true if the event was committed
|
||||
*/
|
||||
public native boolean emitEvent(long eventTypeId, long timestamp, long when);
|
||||
|
||||
/**
|
||||
* End recording events, which includes flushing data in thread buffers
|
||||
*
|
||||
* Requires that JFR has been started with {@link #createNativeJFR()}
|
||||
*
|
||||
*/
|
||||
public native void endRecording();
|
||||
|
||||
/**
|
||||
* Return a list of all classes deriving from {@link Event}
|
||||
*
|
||||
* @return list of event classes.
|
||||
*/
|
||||
public native List<Class<? extends Event>> getAllEventClasses();
|
||||
|
||||
/**
|
||||
* Return a count of the number of unloaded classes deriving from {@link Event}
|
||||
*
|
||||
* @return number of unloaded event classes.
|
||||
*/
|
||||
public native long getUnloadedEventClassCount();
|
||||
|
||||
/**
|
||||
* Return a unique identifier for a class. The class is marked as being
|
||||
* "in use" in JFR.
|
||||
*
|
||||
* @param clazz clazz
|
||||
*
|
||||
* @return a unique class identifier
|
||||
*/
|
||||
// @HotSpotIntrinsicCandidate
|
||||
public static native long getClassId(Class<?> clazz);
|
||||
|
||||
// temporary workaround until we solve intrinsics supporting epoch shift tagging
|
||||
public static native long getClassIdNonIntrinsic(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Return process identifier.
|
||||
*
|
||||
* @return process identifier
|
||||
*/
|
||||
public native String getPid();
|
||||
|
||||
/**
|
||||
* Return unique identifier for stack trace.
|
||||
*
|
||||
* Requires that JFR has been started with {@link #createNativeJFR()}
|
||||
*
|
||||
* @param skipCount number of frames to skip
|
||||
* @return a unique stack trace identifier
|
||||
*/
|
||||
public native long getStackTraceId(int skipCount);
|
||||
|
||||
/**
|
||||
* Return identifier for thread
|
||||
*
|
||||
* @param t thread
|
||||
* @return a unique thread identifier
|
||||
*/
|
||||
public native long getThreadId(Thread t);
|
||||
|
||||
/**
|
||||
* Frequency, ticks per second
|
||||
*
|
||||
* @return frequency
|
||||
*/
|
||||
public native long getTicksFrequency();
|
||||
|
||||
/**
|
||||
* Write message to log. Should swallow null or empty message, and be able
|
||||
* to handle any Java character and not crash with very large message
|
||||
*
|
||||
* @param tagSetId the tagset id
|
||||
* @param level on level
|
||||
* @param message log message
|
||||
*
|
||||
*/
|
||||
public static native void log(int tagSetId, int level, String message);
|
||||
|
||||
/**
|
||||
* Check whether the logger would output on the given level
|
||||
*
|
||||
* @param level on level
|
||||
* @return {@literal true} if the logger would output on the given level
|
||||
*/
|
||||
public static native boolean shouldLog(int level);
|
||||
|
||||
/**
|
||||
* Subscribe to LogLevel updates for LogTag
|
||||
*
|
||||
* @param lt the log tag to subscribe
|
||||
* @param tagSetId the tagset id
|
||||
*/
|
||||
public static native void subscribeLogLevel(LogTag lt, int tagSetId);
|
||||
|
||||
/**
|
||||
* Call to invoke event tagging and retransformation of the passed classes
|
||||
*
|
||||
* @param classes
|
||||
*/
|
||||
public native synchronized void retransformClasses(Class<?>[] classes);
|
||||
|
||||
/**
|
||||
* Enable event
|
||||
*
|
||||
* @param eventTypeId event type id
|
||||
*
|
||||
* @param enabled enable event
|
||||
*/
|
||||
public native void setEnabled(long eventTypeId, boolean enabled);
|
||||
|
||||
/**
|
||||
* Interval at which the JVM should notify on {@link #FILE_DELTA_CHANGE}
|
||||
*
|
||||
* @param delta number of bytes, reset after file rotation
|
||||
*/
|
||||
public native void setFileNotification(long delta);
|
||||
|
||||
/**
|
||||
* Set the number of global buffers to use
|
||||
*
|
||||
* @param count
|
||||
*
|
||||
* @throws IllegalArgumentException if count is not within a valid range
|
||||
* @throws IllegalStateException if value can't be changed
|
||||
*/
|
||||
public native void setGlobalBufferCount(long count) throws IllegalArgumentException, IllegalStateException;
|
||||
|
||||
/**
|
||||
* Set size of a global buffer
|
||||
*
|
||||
* @param size
|
||||
*
|
||||
* @throws IllegalArgumentException if buffer size is not within a valid
|
||||
* range
|
||||
*/
|
||||
public native void setGlobalBufferSize(long size) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Set overall memory size
|
||||
*
|
||||
* @param size
|
||||
*
|
||||
* @throws IllegalArgumentException if memory size is not within a valid
|
||||
* range
|
||||
*/
|
||||
public native void setMemorySize(long size) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Set interval for method samples, in milliseconds.
|
||||
*
|
||||
* Setting interval to 0 turns off the method sampler.
|
||||
*
|
||||
* @param intervalMillis the sampling interval
|
||||
*/
|
||||
public native void setMethodSamplingInterval(long type, long intervalMillis);
|
||||
|
||||
/**
|
||||
* Sets the file where data should be written.
|
||||
*
|
||||
* Requires that JFR has been started with {@link #createNativeJFR()}
|
||||
*
|
||||
* <pre>
|
||||
* Recording Previous Current Action
|
||||
* ==============================================
|
||||
* true null null Ignore, keep recording in-memory
|
||||
* true null file1 Start disk recording
|
||||
* true file null Copy out metadata to disk and continue in-memory recording
|
||||
* true file1 file2 Copy out metadata and start with new File (file2)
|
||||
* false * null Ignore, but start recording to memory with {@link #beginRecording()}
|
||||
* false * file Ignore, but start recording to disk with {@link #beginRecording()}
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* recording can be set to true/false with {@link #beginRecording()}
|
||||
* {@link #endRecording()}
|
||||
*
|
||||
* @param file the file where data should be written, or null if it should
|
||||
* not be copied out (in memory).
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public native void setOutput(String file);
|
||||
|
||||
/**
|
||||
* Controls if a class deriving from jdk.jfr.Event should
|
||||
* always be instrumented on class load.
|
||||
*
|
||||
* @param force, true to force initialization, false otherwise
|
||||
*/
|
||||
public native void setForceInstrumentation(boolean force);
|
||||
|
||||
/**
|
||||
* Turn on/off thread sampling.
|
||||
*
|
||||
* @param sampleThreads true if threads should be sampled, false otherwise.
|
||||
*
|
||||
* @throws IllegalStateException if state can't be changed.
|
||||
*/
|
||||
public native void setSampleThreads(boolean sampleThreads) throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Turn on/off compressed integers.
|
||||
*
|
||||
* @param compressed true if compressed integers should be used, false
|
||||
* otherwise.
|
||||
*
|
||||
* @throws IllegalStateException if state can't be changed.
|
||||
*/
|
||||
public native void setCompressedIntegers(boolean compressed) throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Set stack depth.
|
||||
*
|
||||
* @param depth
|
||||
*
|
||||
* @throws IllegalArgumentException if not within a valid range
|
||||
* @throws IllegalStateException if depth can't be changed
|
||||
*/
|
||||
public native void setStackDepth(int depth) throws IllegalArgumentException, IllegalStateException;
|
||||
|
||||
/**
|
||||
* Turn on stack trace for an event
|
||||
*
|
||||
* @param eventTypeId the event id
|
||||
*
|
||||
* @param enabled if stack traces should be enabled
|
||||
*/
|
||||
public native void setStackTraceEnabled(long eventTypeId, boolean enabled);
|
||||
|
||||
/**
|
||||
* Set thread buffer size.
|
||||
*
|
||||
* @param size
|
||||
*
|
||||
* @throws IllegalArgumentException if size is not within a valid range
|
||||
* @throws IllegalStateException if size can't be changed
|
||||
*/
|
||||
public native void setThreadBufferSize(long size) throws IllegalArgumentException, IllegalStateException;
|
||||
|
||||
/**
|
||||
* Set threshold for event,
|
||||
*
|
||||
* Long.MAXIMUM_VALUE = no limit
|
||||
*
|
||||
* @param eventTypeId the id of the event type
|
||||
* @param ticks threshold in ticks,
|
||||
* @return true, if it could be set
|
||||
*/
|
||||
public native boolean setThreshold(long eventTypeId, long ticks);
|
||||
|
||||
/**
|
||||
* Store the metadata descriptor that is to be written at the end of a
|
||||
* chunk, data should be written after GMT offset and size of metadata event
|
||||
* should be adjusted
|
||||
*
|
||||
* Requires that JFR has been started with {@link #createNativeJFR()}
|
||||
*
|
||||
* @param bytes binary representation of metadata descriptor
|
||||
*
|
||||
* @param binary representation of descriptor
|
||||
*/
|
||||
public native void storeMetadataDescriptor(byte[] bytes);
|
||||
|
||||
public void endRecording_() {
|
||||
endRecording();
|
||||
recording = false;
|
||||
}
|
||||
|
||||
public void beginRecording_() {
|
||||
beginRecording();
|
||||
recording = true;
|
||||
}
|
||||
|
||||
public boolean isRecording() {
|
||||
return recording;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the JVM supports JVM TI and retransformation has not been disabled this
|
||||
* method will return true. This flag can not change during the lifetime of
|
||||
* the JVM.
|
||||
*
|
||||
* @return if transform is allowed
|
||||
*/
|
||||
public native boolean getAllowedToDoEventRetransforms();
|
||||
|
||||
/**
|
||||
* Set up native resources, data structures, threads etc. for JFR
|
||||
*
|
||||
* @param simulateFailure simulate a initialization failure and rollback in
|
||||
* native, used for testing purposes
|
||||
*
|
||||
* @throws IllegalStateException if native part of JFR could not be created.
|
||||
*
|
||||
*/
|
||||
private native boolean createJFR(boolean simulateFailure) throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Destroys native part of JFR. If already destroy, call is ignored.
|
||||
*
|
||||
* Requires that JFR has been started with {@link #createNativeJFR()}
|
||||
*
|
||||
* @return if an instance was actually destroyed.
|
||||
*
|
||||
*/
|
||||
private native boolean destroyJFR();
|
||||
|
||||
public boolean createFailedNativeJFR() throws IllegalStateException {
|
||||
return createJFR(true);
|
||||
}
|
||||
|
||||
public void createNativeJFR() {
|
||||
nativeOK = createJFR(false);
|
||||
}
|
||||
|
||||
public boolean destroyNativeJFR() {
|
||||
boolean result = destroyJFR();
|
||||
nativeOK = !result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean hasNativeJFR() {
|
||||
return nativeOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cheap test to check if JFR functionality is available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public native boolean isAvailable();
|
||||
|
||||
/**
|
||||
* To convert ticks to wall clock time.
|
||||
*/
|
||||
public native double getTimeConversionFactor();
|
||||
|
||||
/**
|
||||
* Return a unique identifier for a class. Compared to {@link #getClassId()}
|
||||
* , this method does not tag the class as being "in-use".
|
||||
*
|
||||
* @param clazz class
|
||||
*
|
||||
* @return a unique class identifier
|
||||
*/
|
||||
public native long getTypeId(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Fast path fetching the EventWriter using VM intrinsics
|
||||
*
|
||||
* @return thread local EventWriter
|
||||
*/
|
||||
// @HotSpotIntrinsicCandidate
|
||||
public static native Object getEventWriter();
|
||||
|
||||
/**
|
||||
* Create a new EventWriter
|
||||
*
|
||||
* @return thread local EventWriter
|
||||
*/
|
||||
public static native EventWriter newEventWriter();
|
||||
|
||||
/**
|
||||
* Flushes the EventWriter for this thread.
|
||||
*/
|
||||
public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize);
|
||||
|
||||
/**
|
||||
* Sets the location of the disk repository, to be used at an emergency
|
||||
* dump.
|
||||
*
|
||||
* @param dirText
|
||||
*/
|
||||
public native void setRepositoryLocation(String dirText);
|
||||
|
||||
/**
|
||||
* Access to VM termination support.
|
||||
*
|
||||
*@param errorMsg descriptive message to be include in VM termination sequence
|
||||
*/
|
||||
public native void abort(String errorMsg);
|
||||
|
||||
/**
|
||||
* Adds a string to the string constant pool.
|
||||
*
|
||||
* If the same string is added twice, two entries will be created.
|
||||
*
|
||||
* @param id identifier associated with the string, not negative
|
||||
*
|
||||
* @param s string constant to be added, not null
|
||||
*
|
||||
* @return the current epoch of this insertion attempt
|
||||
*/
|
||||
public static native boolean addStringConstant(boolean epoch, long id, String s);
|
||||
/**
|
||||
* Gets the address of the jboolean epoch.
|
||||
*
|
||||
* The epoch alternates every checkpoint.
|
||||
*
|
||||
* @return The address of the jboolean.
|
||||
*/
|
||||
public native long getEpochAddress();
|
||||
|
||||
public native void uncaughtException(Thread thread, Throwable t);
|
||||
/**
|
||||
* Sets cutoff for event.
|
||||
*
|
||||
* Determines how long the event should be allowed to run.
|
||||
*
|
||||
* Long.MAXIMUM_VALUE = no limit
|
||||
*
|
||||
* @param eventTypeId the id of the event type
|
||||
* @param cutoffTicks cutoff in ticks,
|
||||
* @return true, if it could be set
|
||||
*/
|
||||
public native boolean setCutoff(long eventTypeId, long cutoffTicks);
|
||||
|
||||
/**
|
||||
* Emit old object sample events.
|
||||
*
|
||||
* @param cutoff the cutoff in ticks
|
||||
* @param emitAll emit all samples in old object queue
|
||||
*/
|
||||
public native void emitOldObjectSamples(long cutoff, boolean emitAll);
|
||||
|
||||
/**
|
||||
* Test if a chunk rotation is warranted.
|
||||
*
|
||||
* @return if it is time to perform a chunk rotation
|
||||
*/
|
||||
public native boolean shouldRotateDisk();
|
||||
}
|
||||
86
jdkSrc/jdk8/jdk/jfr/internal/JVMSupport.java
Normal file
86
jdkSrc/jdk8/jdk/jfr/internal/JVMSupport.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Checks if the running VM supports Flight Recorder.
|
||||
*
|
||||
* Purpose of this helper class is to detect early and cleanly if the VM has
|
||||
* support for Flight Recorder, i.e. not throw {@link UnsatisfiedLinkError} in
|
||||
* unexpected places.
|
||||
* <p>
|
||||
* This is needed so a disabled-jfr.jar can be built for non Oracle JDKs.
|
||||
*/
|
||||
public final class JVMSupport {
|
||||
|
||||
private static final String UNSUPPORTED_VM_MESSAGE = "Flight Recorder is not supported on this VM";
|
||||
private static final boolean notAvailable = !checkAvailability();
|
||||
|
||||
private static boolean checkAvailability() {
|
||||
// set jfr.unsupported.vm to true to test API on an unsupported VM
|
||||
try {
|
||||
if (SecuritySupport.getBooleanProperty("jfr.unsupported.vm")) {
|
||||
return false;
|
||||
}
|
||||
} catch (NoClassDefFoundError cnfe) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// Will typically throw UnsatisfiedLinkError if
|
||||
// there is no native implementation
|
||||
JVM.getJVM().isAvailable();
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureWithInternalError() {
|
||||
if (notAvailable) {
|
||||
throw new InternalError(UNSUPPORTED_VM_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureWithIOException() throws IOException {
|
||||
if (notAvailable) {
|
||||
throw new IOException(UNSUPPORTED_VM_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureWithIllegalStateException() {
|
||||
if (notAvailable) {
|
||||
throw new IllegalStateException(UNSUPPORTED_VM_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNotAvailable() {
|
||||
return notAvailable;
|
||||
}
|
||||
|
||||
public static void tryToInitializeJVM() {
|
||||
}
|
||||
}
|
||||
152
jdkSrc/jdk8/jdk/jfr/internal/JVMUpcalls.java
Normal file
152
jdkSrc/jdk8/jdk/jfr/internal/JVMUpcalls.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
import jdk.jfr.internal.instrument.JDKEvents;
|
||||
|
||||
/**
|
||||
* All upcalls from the JVM should go through this class.
|
||||
*
|
||||
*/
|
||||
// Called by native
|
||||
final class JVMUpcalls {
|
||||
/**
|
||||
* Called by the JVM when a retransform happens on a tagged class
|
||||
*
|
||||
* @param traceId
|
||||
* Id of the class
|
||||
* @param dummy
|
||||
* (not used but needed since invoke infrastructure in native
|
||||
* uses same signature bytesForEagerInstrumentation)
|
||||
* @param clazz
|
||||
* class being retransformed
|
||||
* @param oldBytes
|
||||
* byte code
|
||||
* @return byte code to use
|
||||
* @throws Throwable
|
||||
*/
|
||||
static byte[] onRetransform(long traceId, boolean dummy, Class<?> clazz, byte[] oldBytes) throws Throwable {
|
||||
try {
|
||||
if (Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
|
||||
EventHandler handler = Utils.getHandler(clazz.asSubclass(Event.class));
|
||||
if (handler == null) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event handler found for " + clazz.getName() + ". Ignoring instrumentation request.");
|
||||
// Probably triggered by some other agent
|
||||
return oldBytes;
|
||||
}
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding instrumentation to event class " + clazz.getName() + " using retransform");
|
||||
EventInstrumentation ei = new EventInstrumentation(clazz.getSuperclass(), oldBytes, traceId);
|
||||
byte[] bytes = ei.buildInstrumented();
|
||||
ASMToolkit.logASM(clazz.getName(), bytes);
|
||||
return bytes;
|
||||
}
|
||||
return JDKEvents.retransformCallback(clazz, oldBytes);
|
||||
} catch (Throwable t) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation to event class " + clazz.getName());
|
||||
}
|
||||
return oldBytes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the JVM when requested to do an "eager" instrumentation. Would
|
||||
* normally happen when JVMTI retransform capabilities are not available.
|
||||
*
|
||||
* @param traceId
|
||||
* Id of the class
|
||||
* @param forceInstrumentation
|
||||
* add instrumentation regardless if event is enabled or not.
|
||||
* @param superClazz
|
||||
* the super class of the class being processed
|
||||
* @param oldBytes
|
||||
* byte code
|
||||
* @return byte code to use
|
||||
* @throws Throwable
|
||||
*/
|
||||
static byte[] bytesForEagerInstrumentation(long traceId, boolean forceInstrumentation, Class<?> superClass, byte[] oldBytes) throws Throwable {
|
||||
if (JVMSupport.isNotAvailable()) {
|
||||
return oldBytes;
|
||||
}
|
||||
String eventName = "<Unknown>";
|
||||
try {
|
||||
EventInstrumentation ei = new EventInstrumentation(superClass, oldBytes, traceId);
|
||||
eventName = ei.getEventName();
|
||||
if (!forceInstrumentation) {
|
||||
// Assume we are recording
|
||||
MetadataRepository mr = MetadataRepository.getInstance();
|
||||
// No need to generate bytecode if:
|
||||
// 1) Event class is disabled, and there is not an external configuration that overrides.
|
||||
// 2) Event class has @Registered(false)
|
||||
if (!mr.isEnabled(ei.getEventName()) && !ei.isEnabled() || !ei.isRegistered()) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Skipping instrumentation for event type " + eventName + " since event was disabled on class load");
|
||||
return oldBytes;
|
||||
}
|
||||
}
|
||||
// Corner case when we are forced to generate bytecode. We can't reference the event
|
||||
// handler in #isEnabled() before event class has been registered, so we add a
|
||||
// guard against a null reference.
|
||||
ei.setGuardHandler(true);
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
|
||||
EventHandlerCreator eh = new EventHandlerCreator(traceId, ei.getSettingInfos(), ei.getFieldInfos());
|
||||
// Handler class must be loaded before instrumented event class can
|
||||
// be used
|
||||
eh.makeEventHandlerClass();
|
||||
byte[] bytes = ei.buildInstrumented();
|
||||
ASMToolkit.logASM(ei.getClassName() + "(" + traceId + ")", bytes);
|
||||
return bytes;
|
||||
} catch (Throwable t) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation for event type " + eventName);
|
||||
return oldBytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the JVM to create the recorder thread.
|
||||
*
|
||||
* @param systemThreadGroup
|
||||
* the system thread group
|
||||
*
|
||||
* @param contextClassLoader
|
||||
* the context class loader.
|
||||
*
|
||||
* @return a new thread
|
||||
*/
|
||||
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
|
||||
return SecuritySupport.createRecorderThread(systemThreadGroup, contextClassLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the JVM to initialize the EventHandlerProxy class.
|
||||
*
|
||||
* @return the EventHandlerProxy class
|
||||
*/
|
||||
static Class<? extends EventHandler> getEventHandlerProxyClass() {
|
||||
return EventHandlerProxyCreator.proxyClass;
|
||||
}
|
||||
}
|
||||
41
jdkSrc/jdk8/jdk/jfr/internal/LogLevel.java
Normal file
41
jdkSrc/jdk8/jdk/jfr/internal/LogLevel.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public enum LogLevel {
|
||||
TRACE(1),
|
||||
DEBUG(2),
|
||||
INFO(3),
|
||||
WARN(4),
|
||||
ERROR(5);
|
||||
// must be in sync with JVM levels.
|
||||
|
||||
final int level;
|
||||
|
||||
LogLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
}
|
||||
90
jdkSrc/jdk8/jdk/jfr/internal/LogTag.java
Normal file
90
jdkSrc/jdk8/jdk/jfr/internal/LogTag.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* Mapped against c++ enum in jfrLogTagSet.hpp */
|
||||
public enum LogTag {
|
||||
/**
|
||||
* Covers
|
||||
* <ul>
|
||||
* <li>Initialization of Flight Recorder
|
||||
* <li> recording life cycle (start, stop and dump)
|
||||
* <li> repository life cycle
|
||||
* <li>loading of configuration files.
|
||||
* </ul>
|
||||
* Target audience: operations
|
||||
*/
|
||||
JFR(0),
|
||||
/**
|
||||
* Covers general implementation aspects of JFR (for Hotspot developers)
|
||||
*/
|
||||
JFR_SYSTEM(1),
|
||||
/**
|
||||
* Covers JVM/JDK events (for Hotspot developers)
|
||||
*/
|
||||
JFR_SYSTEM_EVENT(2),
|
||||
/**
|
||||
* Covers setting for the JVM/JDK (for Hotspot developers)
|
||||
*/
|
||||
JFR_SYSTEM_SETTING(3),
|
||||
/**
|
||||
* Covers generated bytecode (for Hotspot developers)
|
||||
*/
|
||||
JFR_SYSTEM_BYTECODE(4),
|
||||
/**
|
||||
* Covers XML parsing (for Hotspot developers)
|
||||
*/
|
||||
JFR_SYSTEM_PARSER(5),
|
||||
/**
|
||||
* Covers metadata for JVM/JDK (for Hotspot developers)
|
||||
*/
|
||||
JFR_SYSTEM_METADATA(6),
|
||||
/**
|
||||
* Covers metadata for Java user (for Hotspot developers)
|
||||
*/
|
||||
JFR_METADATA(7),
|
||||
/**
|
||||
* Covers events (for users of the JDK)
|
||||
*/
|
||||
JFR_EVENT(8),
|
||||
/**
|
||||
* Covers setting (for users of the JDK)
|
||||
*/
|
||||
JFR_SETTING(9),
|
||||
/**
|
||||
* Covers usage of jcmd with JFR
|
||||
*/
|
||||
JFR_DCMD(10);
|
||||
|
||||
/* set from native side */
|
||||
volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized
|
||||
|
||||
final int id;
|
||||
|
||||
LogTag(int tagId) {
|
||||
id = tagId;
|
||||
}
|
||||
}
|
||||
67
jdkSrc/jdk8/jdk/jfr/internal/Logger.java
Normal file
67
jdkSrc/jdk8/jdk/jfr/internal/Logger.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* JFR logger
|
||||
*
|
||||
*/
|
||||
|
||||
public final class Logger {
|
||||
|
||||
private final static int MAX_SIZE = 10000;
|
||||
static {
|
||||
// This will try to initialize the JVM logging system
|
||||
JVMSupport.tryToInitializeJVM();
|
||||
}
|
||||
|
||||
|
||||
public static void log(LogTag logTag, LogLevel logLevel, String message) {
|
||||
if (shouldLog(logTag, logLevel)) {
|
||||
logInternal(logTag, logLevel, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(LogTag logTag, LogLevel logLevel, Supplier<String> messageSupplier) {
|
||||
if (shouldLog(logTag, logLevel)) {
|
||||
logInternal(logTag, logLevel, messageSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
private static void logInternal(LogTag logTag, LogLevel logLevel, String message) {
|
||||
if (message == null || message.length() < MAX_SIZE) {
|
||||
JVM.log(logTag.id, logLevel.level, message);
|
||||
} else {
|
||||
JVM.log(logTag.id, logLevel.level, message.substring(0, MAX_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldLog(LogTag tag, LogLevel level) {
|
||||
return JVM.shouldLog(level.level);
|
||||
}
|
||||
}
|
||||
273
jdkSrc/jdk8/jdk/jfr/internal/MetadataDescriptor.java
Normal file
273
jdkSrc/jdk8/jdk/jfr/internal/MetadataDescriptor.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
|
||||
/**
|
||||
* Metadata about a chunk
|
||||
*/
|
||||
public final class MetadataDescriptor {
|
||||
|
||||
static final class Attribute {
|
||||
final String name;
|
||||
final String value;
|
||||
|
||||
private Attribute(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Element {
|
||||
final String name;
|
||||
final List<Element> elements = new ArrayList<>();
|
||||
final List<Attribute> attributes = new ArrayList<>();
|
||||
|
||||
Element(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
long longValue(String name) {
|
||||
String v = attribute(name);
|
||||
if (v != null)
|
||||
return Long.parseLong(v);
|
||||
else
|
||||
throw new IllegalArgumentException(name);
|
||||
}
|
||||
|
||||
String attribute(String name) {
|
||||
for (Attribute a : attributes) {
|
||||
if (a.name.equals(name)) {
|
||||
return a.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
prettyPrintXML(sb, "", this);
|
||||
} catch (IOException e) {
|
||||
// should not happen
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
long attribute(String name, long defaultValue) {
|
||||
String text = attribute(name);
|
||||
if (text == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Long.parseLong(text);
|
||||
}
|
||||
|
||||
String attribute(String name, String defaultValue) {
|
||||
String text = attribute(name);
|
||||
if (text == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
List<Element> elements(String... names) {
|
||||
List<Element> filteredElements = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
for (Element e : elements) {
|
||||
if (e.name.equals(name)) {
|
||||
filteredElements.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredElements;
|
||||
}
|
||||
|
||||
void add(Element element) {
|
||||
elements.add(element);
|
||||
}
|
||||
|
||||
void addAttribute(String name, Object value) {
|
||||
attributes.add(new Attribute(name, String.valueOf(value)));
|
||||
}
|
||||
|
||||
Element newChild(String name) {
|
||||
Element e = new Element(name);
|
||||
elements.add(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
public void addArrayAttribute(Element element, String name, Object value) {
|
||||
String typeName = value.getClass().getComponentType().getName();
|
||||
switch (typeName) {
|
||||
case "int":
|
||||
int[] ints = (int[]) value;
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
addAttribute(name + "-" + i , ints[i]);
|
||||
}
|
||||
break;
|
||||
case "long":
|
||||
long[] longs = (long[]) value;
|
||||
for (int i = 0; i < longs.length; i++) {
|
||||
addAttribute(name + "-" + i , longs[i]);
|
||||
}
|
||||
break;
|
||||
case "float":
|
||||
float[] floats = (float[]) value;
|
||||
for (int i = 0; i < floats.length; i++) {
|
||||
addAttribute(name + "-" + i , floats[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case "double":
|
||||
double[] doubles = (double[]) value;
|
||||
for (int i = 0; i < doubles.length; i++) {
|
||||
addAttribute(name + "-" + i , doubles[i]);
|
||||
}
|
||||
break;
|
||||
case "short":
|
||||
short[] shorts = (short[]) value;
|
||||
for (int i = 0; i < shorts.length; i++) {
|
||||
addAttribute(name + "-" + i , shorts[i]);
|
||||
}
|
||||
break;
|
||||
case "char":
|
||||
char[] chars = (char[]) value;
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
addAttribute(name + "-" + i , chars[i]);
|
||||
}
|
||||
break;
|
||||
case "byte":
|
||||
byte[] bytes = (byte[]) value;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
addAttribute(name + "-" + i , bytes[i]);
|
||||
}
|
||||
break;
|
||||
case "boolean":
|
||||
boolean[] booleans = (boolean[]) value;
|
||||
for (int i = 0; i < booleans.length; i++) {
|
||||
addAttribute(name + "-" + i , booleans[i]);
|
||||
}
|
||||
break;
|
||||
case "java.lang.String":
|
||||
String[] strings = (String[]) value;
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
addAttribute(name + "-" + i , strings[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InternalError("Array type of " + typeName + " is not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final String ATTRIBUTE_ID = "id";
|
||||
static final String ATTRIBUTE_SIMPLE_TYPE = "simpleType";
|
||||
static final String ATTRIBUTE_GMT_OFFSET = "gmtOffset";
|
||||
static final String ATTRIBUTE_LOCALE = "locale";
|
||||
static final String ELEMENT_TYPE = "class";
|
||||
static final String ELEMENT_SETTING = "setting";
|
||||
static final String ELEMENT_ANNOTATION = "annotation";
|
||||
static final String ELEMENT_FIELD = "field";
|
||||
static final String ATTRIBUTE_SUPER_TYPE = "superType";
|
||||
static final String ATTRIBUTE_TYPE_ID = "class";
|
||||
static final String ATTRIBUTE_DIMENSION = "dimension";
|
||||
static final String ATTRIBUTE_NAME = "name";
|
||||
static final String ATTRIBUTE_CONSTANT_POOL = "constantPool";
|
||||
static final String ATTRIBUTE_DEFAULT_VALUE = "defaultValue";
|
||||
|
||||
final List<EventType> eventTypes = new ArrayList<>();
|
||||
final Collection<Type> types = new ArrayList<>();
|
||||
long gmtOffset;
|
||||
String locale;
|
||||
Element root;
|
||||
|
||||
// package private
|
||||
MetadataDescriptor() {
|
||||
}
|
||||
|
||||
private static void prettyPrintXML(Appendable sb, String indent, Element e) throws IOException {
|
||||
sb.append(indent + "<" + e.name);
|
||||
for (Attribute a : e.attributes) {
|
||||
sb.append(" ").append(a.name).append("=\"").append(a.value).append("\"");
|
||||
}
|
||||
if (e.elements.size() == 0) {
|
||||
sb.append("/");
|
||||
}
|
||||
sb.append(">\n");
|
||||
for (Element child : e.elements) {
|
||||
prettyPrintXML(sb, indent + " ", child);
|
||||
}
|
||||
if (e.elements.size() != 0) {
|
||||
sb.append(indent).append("</").append(e.name).append(">\n");
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Type> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
public List<EventType> getEventTypes() {
|
||||
return eventTypes;
|
||||
}
|
||||
|
||||
public int getGMTOffset() {
|
||||
return (int) gmtOffset;
|
||||
}
|
||||
|
||||
public String getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public static MetadataDescriptor read(DataInput input) throws IOException {
|
||||
MetadataReader r = new MetadataReader(input);
|
||||
return r.getDescriptor();
|
||||
}
|
||||
|
||||
static void write(List<Type> types, DataOutput output) throws IOException {
|
||||
MetadataDescriptor m = new MetadataDescriptor();
|
||||
m.locale = Locale.getDefault().toString();
|
||||
m.gmtOffset = TimeZone.getDefault().getRawOffset();
|
||||
m.types.addAll(types);
|
||||
MetadataWriter w = new MetadataWriter(m);
|
||||
w.writeBinary(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return root.toString();
|
||||
}
|
||||
}
|
||||
421
jdkSrc/jdk8/jdk/jfr/internal/MetadataHandler.java
Normal file
421
jdkSrc/jdk8/jdk/jfr/internal/MetadataHandler.java
Normal file
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.org.xml.sax.Attributes;
|
||||
import jdk.internal.org.xml.sax.EntityResolver;
|
||||
import jdk.internal.org.xml.sax.SAXException;
|
||||
import jdk.internal.org.xml.sax.helpers.DefaultHandler;
|
||||
import jdk.internal.util.xml.SAXParser;
|
||||
import jdk.internal.util.xml.impl.SAXParserImpl;
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Category;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Enabled;
|
||||
import jdk.jfr.Experimental;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.Period;
|
||||
import jdk.jfr.Relational;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.Threshold;
|
||||
import jdk.jfr.TransitionFrom;
|
||||
import jdk.jfr.TransitionTo;
|
||||
import jdk.jfr.Unsigned;
|
||||
|
||||
final class MetadataHandler extends DefaultHandler implements EntityResolver {
|
||||
|
||||
static class TypeElement {
|
||||
List<FieldElement> fields = new ArrayList<>();
|
||||
String name;
|
||||
String label;
|
||||
String description;
|
||||
String category;
|
||||
String superType;
|
||||
String period;
|
||||
boolean thread;
|
||||
boolean startTime;
|
||||
boolean stackTrace;
|
||||
boolean cutoff;
|
||||
boolean isEvent;
|
||||
boolean experimental;
|
||||
boolean valueType;
|
||||
}
|
||||
|
||||
static class FieldElement {
|
||||
TypeElement referenceType;
|
||||
String name;
|
||||
String label;
|
||||
String description;
|
||||
String contentType;
|
||||
String typeName;
|
||||
String transition;
|
||||
String relation;
|
||||
boolean struct;
|
||||
boolean array;
|
||||
boolean experimental;
|
||||
boolean unsigned;
|
||||
}
|
||||
|
||||
static class XmlType {
|
||||
String name;
|
||||
String javaType;
|
||||
String contentType;
|
||||
boolean unsigned;
|
||||
}
|
||||
|
||||
final Map<String, TypeElement> types = new LinkedHashMap<>(200);
|
||||
final Map<String, XmlType> xmlTypes = new HashMap<>(20);
|
||||
final Map<String, List<AnnotationElement>> xmlContentTypes = new HashMap<>(20);
|
||||
final List<String> relations = new ArrayList<>();
|
||||
long eventTypeId = 255;
|
||||
long structTypeId = 33;
|
||||
FieldElement currentField;
|
||||
TypeElement currentType;
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
switch (qName) {
|
||||
case "XmlType":
|
||||
XmlType xmlType = new XmlType();
|
||||
xmlType.name = attributes.getValue("name");
|
||||
xmlType.javaType = attributes.getValue("javaType");
|
||||
xmlType.contentType = attributes.getValue("contentType");
|
||||
xmlType.unsigned = Boolean.valueOf(attributes.getValue("unsigned"));
|
||||
xmlTypes.put(xmlType.name, xmlType);
|
||||
break;
|
||||
case "Type":
|
||||
case "Event":
|
||||
currentType = new TypeElement();
|
||||
currentType.name = attributes.getValue("name");
|
||||
currentType.label = attributes.getValue("label");
|
||||
currentType.description = attributes.getValue("description");
|
||||
currentType.category = attributes.getValue("category");
|
||||
currentType.thread = getBoolean(attributes, "thread", false);
|
||||
currentType.stackTrace = getBoolean(attributes, "stackTrace", false);
|
||||
currentType.startTime = getBoolean(attributes, "startTime", true);
|
||||
currentType.period = attributes.getValue("period");
|
||||
currentType.cutoff = getBoolean(attributes, "cutoff", false);
|
||||
currentType.experimental = getBoolean(attributes, "experimental", false);
|
||||
currentType.isEvent = qName.equals("Event");
|
||||
break;
|
||||
case "Field":
|
||||
currentField = new FieldElement();
|
||||
currentField.struct = getBoolean(attributes, "struct", false);
|
||||
currentField.array = getBoolean(attributes, "array", false);
|
||||
currentField.name = attributes.getValue("name");
|
||||
currentField.label = attributes.getValue("label");
|
||||
currentField.typeName = attributes.getValue("type");
|
||||
currentField.description = attributes.getValue("description");
|
||||
currentField.experimental = getBoolean(attributes, "experimental", false);
|
||||
currentField.contentType = attributes.getValue("contentType");
|
||||
currentField.relation = attributes.getValue("relation");
|
||||
currentField.transition = attributes.getValue("transition");
|
||||
break;
|
||||
case "XmlContentType":
|
||||
String name = attributes.getValue("name");
|
||||
String annotation = attributes.getValue("annotation");
|
||||
xmlContentTypes.put(name, createAnnotationElements(annotation));
|
||||
break;
|
||||
case "Relation":
|
||||
String n = attributes.getValue("name");
|
||||
relations.add(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private List<AnnotationElement> createAnnotationElements(String annotation) throws InternalError {
|
||||
String[] annotations = annotation.split(",");
|
||||
List<AnnotationElement> annotationElements = new ArrayList<>();
|
||||
for (String a : annotations) {
|
||||
a = a.trim();
|
||||
int leftParenthesis = a.indexOf("(");
|
||||
if (leftParenthesis == -1) {
|
||||
annotationElements.add(new AnnotationElement(createAnnotationClass(a)));
|
||||
} else {
|
||||
int rightParenthesis = a.lastIndexOf(")");
|
||||
if (rightParenthesis == -1) {
|
||||
throw new InternalError("Expected closing parenthesis for 'XMLContentType'");
|
||||
}
|
||||
String value = a.substring(leftParenthesis + 1, rightParenthesis);
|
||||
String type = a.substring(0, leftParenthesis);
|
||||
annotationElements.add(new AnnotationElement(createAnnotationClass(type), value));
|
||||
}
|
||||
}
|
||||
return annotationElements;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends Annotation> createAnnotationClass(String type) {
|
||||
try {
|
||||
if (!type.startsWith("jdk.jfr.")) {
|
||||
throw new IllegalStateException("Incorrect type " + type + ". Annotation class must be located in jdk.jfr package.");
|
||||
}
|
||||
Class<?> c = Class.forName(type, true, null);
|
||||
return (Class<? extends Annotation>) c;
|
||||
} catch (ClassNotFoundException cne) {
|
||||
throw new IllegalStateException(cne);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
|
||||
String value = attributes.getValue(name);
|
||||
return value == null ? defaultValue : Boolean.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) {
|
||||
switch (qName) {
|
||||
case "Type":
|
||||
case "Event":
|
||||
types.put(currentType.name, currentType);
|
||||
currentType = null;
|
||||
break;
|
||||
case "Field":
|
||||
currentType.fields.add(currentField);
|
||||
currentField = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Type> createTypes() throws IOException {
|
||||
SAXParser parser = new SAXParserImpl();
|
||||
MetadataHandler t = new MetadataHandler();
|
||||
try (InputStream is = new BufferedInputStream(SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.xml"))) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Parsing metadata.xml");
|
||||
try {
|
||||
parser.parse(is, t);
|
||||
return t.buildTypes();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Type> buildTypes() {
|
||||
removeXMLConvenience();
|
||||
Map<String, Type> typeMap = buildTypeMap();
|
||||
Map<String, AnnotationElement> relationMap = buildRelationMap(typeMap);
|
||||
addFields(typeMap, relationMap);
|
||||
return trimTypes(typeMap);
|
||||
}
|
||||
|
||||
private Map<String, AnnotationElement> buildRelationMap(Map<String, Type> typeMap) {
|
||||
Map<String, AnnotationElement> relationMap = new HashMap<>();
|
||||
for (String relation : relations) {
|
||||
Type relationType = new Type(Type.TYPES_PREFIX + relation, Type.SUPER_TYPE_ANNOTATION, eventTypeId++);
|
||||
relationType.setAnnotations(Collections.singletonList(new AnnotationElement(Relational.class)));
|
||||
AnnotationElement ae = PrivateAccess.getInstance().newAnnotation(relationType, Collections.emptyList(), true);
|
||||
relationMap.put(relation, ae);
|
||||
typeMap.put(relationType.getName(), relationType);
|
||||
}
|
||||
return relationMap;
|
||||
}
|
||||
|
||||
private List<Type> trimTypes(Map<String, Type> lookup) {
|
||||
List<Type> trimmedTypes = new ArrayList<>(lookup.size());
|
||||
for (Type t : lookup.values()) {
|
||||
t.trimFields();
|
||||
trimmedTypes.add(t);
|
||||
}
|
||||
return trimmedTypes;
|
||||
}
|
||||
|
||||
private void addFields(Map<String, Type> lookup, Map<String, AnnotationElement> relationMap) {
|
||||
for (TypeElement te : types.values()) {
|
||||
Type type = lookup.get(te.name);
|
||||
if (te.isEvent) {
|
||||
boolean periodic = te.period!= null;
|
||||
TypeLibrary.addImplicitFields(type, periodic, te.startTime && !periodic, te.thread, te.stackTrace && !periodic, te.cutoff);
|
||||
}
|
||||
for (FieldElement f : te.fields) {
|
||||
Type fieldType = Type.getKnownType(f.typeName);
|
||||
if (fieldType == null) {
|
||||
fieldType = Objects.requireNonNull(lookup.get(f.referenceType.name));
|
||||
}
|
||||
List<AnnotationElement> aes = new ArrayList<>();
|
||||
if (f.unsigned) {
|
||||
aes.add(new AnnotationElement(Unsigned.class));
|
||||
}
|
||||
if (f.contentType != null) {
|
||||
aes.addAll(Objects.requireNonNull(xmlContentTypes.get(f.contentType)));
|
||||
}
|
||||
if (f.relation != null) {
|
||||
aes.add(Objects.requireNonNull(relationMap.get(f.relation)));
|
||||
}
|
||||
if (f.label != null) {
|
||||
aes.add(new AnnotationElement(Label.class, f.label));
|
||||
}
|
||||
if (f.experimental) {
|
||||
aes.add(new AnnotationElement(Experimental.class));
|
||||
}
|
||||
if (f.description != null) {
|
||||
aes.add(new AnnotationElement(Description.class, f.description));
|
||||
}
|
||||
if ("from".equals(f.transition)) {
|
||||
aes.add(new AnnotationElement(TransitionFrom.class));
|
||||
}
|
||||
if ("to".equals(f.transition)) {
|
||||
aes.add(new AnnotationElement(TransitionTo.class));
|
||||
}
|
||||
boolean constantPool = !f.struct && f.referenceType != null;
|
||||
type.add(PrivateAccess.getInstance().newValueDescriptor(f.name, fieldType, aes, f.array ? 1 : 0, constantPool, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Type> buildTypeMap() {
|
||||
Map<String, Type> typeMap = new HashMap<>();
|
||||
for (Type type : Type.getKnownTypes()) {
|
||||
typeMap.put(type.getName(), type);
|
||||
}
|
||||
|
||||
for (TypeElement t : types.values()) {
|
||||
List<AnnotationElement> aes = new ArrayList<>();
|
||||
if (t.category != null) {
|
||||
aes.add(new AnnotationElement(Category.class, buildCategoryArray(t.category)));
|
||||
}
|
||||
if (t.label != null) {
|
||||
aes.add(new AnnotationElement(Label.class, t.label));
|
||||
}
|
||||
if (t.description != null) {
|
||||
aes.add(new AnnotationElement(Description.class, t.description));
|
||||
}
|
||||
if (t.isEvent) {
|
||||
if (t.period != null) {
|
||||
aes.add(new AnnotationElement(Period.class, t.period));
|
||||
} else {
|
||||
if (t.startTime) {
|
||||
aes.add(new AnnotationElement(Threshold.class, "0 ns"));
|
||||
}
|
||||
if (t.stackTrace) {
|
||||
aes.add(new AnnotationElement(StackTrace.class, true));
|
||||
}
|
||||
}
|
||||
if (t.cutoff) {
|
||||
aes.add(new AnnotationElement(Cutoff.class, Cutoff.INIFITY));
|
||||
}
|
||||
}
|
||||
if (t.experimental) {
|
||||
aes.add(new AnnotationElement(Experimental.class));
|
||||
}
|
||||
Type type;
|
||||
if (t.isEvent) {
|
||||
aes.add(new AnnotationElement(Enabled.class, false));
|
||||
type = new PlatformEventType(t.name, eventTypeId++, false, true);
|
||||
} else {
|
||||
// Struct types had their own XML-element in the past. To have id assigned in the
|
||||
// same order as generated .hpp file do some tweaks here.
|
||||
boolean valueType = t.name.endsWith("StackFrame") || t.valueType;
|
||||
type = new Type(t.name, null, valueType ? eventTypeId++ : nextTypeId(t.name), false);
|
||||
}
|
||||
type.setAnnotations(aes);
|
||||
typeMap.put(t.name, type);
|
||||
}
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
private long nextTypeId(String name) {
|
||||
if (Type.THREAD.getName().equals(name)) {
|
||||
return Type.THREAD.getId();
|
||||
}
|
||||
if (Type.STRING.getName().equals(name)) {
|
||||
return Type.STRING.getId();
|
||||
}
|
||||
if (Type.CLASS.getName().equals(name)) {
|
||||
return Type.CLASS.getId();
|
||||
}
|
||||
for (Type type : Type.getKnownTypes()) {
|
||||
if (type.getName().equals(name)) {
|
||||
return type.getId();
|
||||
}
|
||||
}
|
||||
return structTypeId++;
|
||||
}
|
||||
|
||||
private String[] buildCategoryArray(String category) {
|
||||
List<String> categories = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char c : category.toCharArray()) {
|
||||
if (c == ',') {
|
||||
categories.add(sb.toString().trim());
|
||||
sb.setLength(0);
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
categories.add(sb.toString().trim());
|
||||
return categories.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private void removeXMLConvenience() {
|
||||
for (TypeElement t : types.values()) {
|
||||
XmlType xmlType = xmlTypes.get(t.name);
|
||||
if (xmlType != null && xmlType.javaType != null) {
|
||||
t.name = xmlType.javaType; // known type, i.e primitive
|
||||
} else {
|
||||
if (t.isEvent) {
|
||||
t.name = Type.EVENT_NAME_PREFIX + t.name;
|
||||
} else {
|
||||
t.name = Type.TYPES_PREFIX + t.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (TypeElement t : types.values()) {
|
||||
for (FieldElement f : t.fields) {
|
||||
f.referenceType = types.get(f.typeName);
|
||||
XmlType xmlType = xmlTypes.get(f.typeName);
|
||||
if (xmlType != null) {
|
||||
if (xmlType.javaType != null) {
|
||||
f.typeName = xmlType.javaType;
|
||||
}
|
||||
if (xmlType.contentType != null) {
|
||||
f.contentType = xmlType.contentType;
|
||||
}
|
||||
if (xmlType.unsigned) {
|
||||
f.unsigned = true;
|
||||
}
|
||||
}
|
||||
if (f.struct && f.referenceType != null) {
|
||||
f.referenceType.valueType = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
273
jdkSrc/jdk8/jdk/jfr/internal/MetadataReader.java
Normal file
273
jdkSrc/jdk8/jdk/jfr/internal/MetadataReader.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_CONSTANT_POOL;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DIMENSION;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_ID;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_NAME;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SIMPLE_TYPE;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SUPER_TYPE;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_TYPE_ID;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_ANNOTATION;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_FIELD;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_SETTING;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_TYPE;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.MetadataDescriptor.Element;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
|
||||
/**
|
||||
* Parses metadata.
|
||||
*
|
||||
*/
|
||||
final class MetadataReader {
|
||||
|
||||
private final DataInput input;
|
||||
private final List<String> pool;
|
||||
private final MetadataDescriptor descriptor;
|
||||
private final Map<Long, Type> types = new HashMap<>();
|
||||
|
||||
public MetadataReader(DataInput input) throws IOException {
|
||||
this.input = input;
|
||||
int size = input.readInt();
|
||||
((RecordingInput)input).require(size, "Metadata string pool size %d exceeds available data" );
|
||||
this.pool = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
this.pool.add(input.readUTF());
|
||||
}
|
||||
descriptor = new MetadataDescriptor();
|
||||
Element root = createElement();
|
||||
Element metadata = root.elements("metadata").get(0);
|
||||
declareTypes(metadata);
|
||||
defineTypes(metadata);
|
||||
annotateTypes(metadata);
|
||||
buildEvenTypes();
|
||||
Element time = root.elements("region").get(0);
|
||||
descriptor.gmtOffset = time.attribute(MetadataDescriptor.ATTRIBUTE_GMT_OFFSET, 1);
|
||||
descriptor.locale = time.attribute(MetadataDescriptor.ATTRIBUTE_LOCALE, "");
|
||||
descriptor.root = root;
|
||||
if (Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE)) {
|
||||
List<Type> ts = new ArrayList<>(types.values());
|
||||
Collections.sort(ts, (x,y) -> x.getName().compareTo(y.getName()));
|
||||
for (Type t : ts) {
|
||||
t.log("Found", LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readString() throws IOException {
|
||||
return pool.get(readInt());
|
||||
}
|
||||
|
||||
private int readInt() throws IOException {
|
||||
return input.readInt();
|
||||
}
|
||||
|
||||
private Element createElement() throws IOException {
|
||||
String name = readString();
|
||||
Element e = new Element(name);
|
||||
int attributeCount = readInt();
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
e.addAttribute(readString(), readString());
|
||||
}
|
||||
int childrenCount = readInt();
|
||||
for (int i = 0; i < childrenCount; i++) {
|
||||
e.add(createElement());
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
private void annotateTypes(Element metadata) throws IOException {
|
||||
for (Element typeElement : metadata.elements(ELEMENT_TYPE)) {
|
||||
Type type = getType(ATTRIBUTE_ID, typeElement);
|
||||
ArrayList<AnnotationElement> aes = new ArrayList<>();
|
||||
for (Element annotationElement : typeElement.elements(ELEMENT_ANNOTATION)) {
|
||||
aes.add(makeAnnotation(annotationElement));
|
||||
}
|
||||
aes.trimToSize();
|
||||
type.setAnnotations(aes);
|
||||
|
||||
int index = 0;
|
||||
if (type instanceof PlatformEventType) {
|
||||
List<SettingDescriptor> settings = ((PlatformEventType) type).getAllSettings();
|
||||
for (Element settingElement : typeElement.elements(ELEMENT_SETTING)) {
|
||||
ArrayList<AnnotationElement> annotations = new ArrayList<>();
|
||||
for (Element annotationElement : settingElement.elements(ELEMENT_ANNOTATION)) {
|
||||
annotations.add(makeAnnotation(annotationElement));
|
||||
}
|
||||
annotations.trimToSize();
|
||||
PrivateAccess.getInstance().setAnnotations(settings.get(index), annotations);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
List<ValueDescriptor> fields = type.getFields();
|
||||
for (Element fieldElement : typeElement.elements(ELEMENT_FIELD)) {
|
||||
ArrayList<AnnotationElement> annotations = new ArrayList<>();
|
||||
for (Element annotationElement : fieldElement.elements(ELEMENT_ANNOTATION)) {
|
||||
annotations.add(makeAnnotation(annotationElement));
|
||||
}
|
||||
annotations.trimToSize();
|
||||
PrivateAccess.getInstance().setAnnotations(fields.get(index), annotations);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationElement makeAnnotation(Element annotationElement) throws IOException {
|
||||
Type annotationType = getType(ATTRIBUTE_TYPE_ID, annotationElement);
|
||||
List<Object> values = new ArrayList<>();
|
||||
for (ValueDescriptor v : annotationType.getFields()) {
|
||||
if (v.isArray()) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
int index = 0;
|
||||
while (true) {
|
||||
String text = annotationElement.attribute(v.getName() + "-" + index);
|
||||
if (text == null) {
|
||||
break;
|
||||
}
|
||||
list.add(objectify(v.getTypeName(), text));
|
||||
index++;
|
||||
}
|
||||
Object object = Utils.makePrimitiveArray(v.getTypeName(), list);
|
||||
if (object == null) {
|
||||
throw new IOException("Unsupported type " + list + " in array");
|
||||
}
|
||||
values.add(object);
|
||||
} else {
|
||||
String text = annotationElement.attribute(v.getName());
|
||||
values.add(objectify(v.getTypeName(), text));
|
||||
}
|
||||
}
|
||||
return PrivateAccess.getInstance().newAnnotation(annotationType, values, false);
|
||||
}
|
||||
|
||||
private Object objectify(String typeName, String text) throws IOException {
|
||||
try {
|
||||
switch (typeName) {
|
||||
case "int":
|
||||
return Integer.valueOf(text);
|
||||
case "long":
|
||||
return Long.valueOf(text);
|
||||
case "double":
|
||||
return Double.valueOf(text);
|
||||
case "float":
|
||||
return Float.valueOf(text);
|
||||
case "short":
|
||||
return Short.valueOf(text);
|
||||
case "char":
|
||||
if (text.length() != 1) {
|
||||
throw new IOException("Unexpected size of char");
|
||||
}
|
||||
return text.charAt(0);
|
||||
case "byte":
|
||||
return Byte.valueOf(text);
|
||||
case "boolean":
|
||||
return Boolean.valueOf(text);
|
||||
case "java.lang.String":
|
||||
return text;
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new IOException("Could not parse text representation of " + typeName);
|
||||
}
|
||||
throw new IOException("Unsupported type for annotation " + typeName);
|
||||
}
|
||||
|
||||
private Type getType(String attribute, Element element) {
|
||||
long id = element.longValue(attribute);
|
||||
Type type = types.get(id);
|
||||
if (type == null) {
|
||||
String name = element.attribute("type");
|
||||
throw new IllegalStateException("Type '" + id + "' is not defined for " + name);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private void buildEvenTypes() {
|
||||
for (Type type : descriptor.types) {
|
||||
if (type instanceof PlatformEventType) {
|
||||
descriptor.eventTypes.add(PrivateAccess.getInstance().newEventType((PlatformEventType) type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void defineTypes(Element metadata) {
|
||||
for (Element typeElement : metadata.elements(ELEMENT_TYPE)) {
|
||||
long id = typeElement.attribute(ATTRIBUTE_ID, -1);
|
||||
Type t = types.get(id);
|
||||
for (Element fieldElement : typeElement.elements(ELEMENT_SETTING)) {
|
||||
String name = fieldElement.attribute(ATTRIBUTE_NAME);
|
||||
String defaultValue = fieldElement.attribute(ATTRIBUTE_NAME);
|
||||
Type settingType = getType(ATTRIBUTE_TYPE_ID, fieldElement);
|
||||
PlatformEventType eventType = (PlatformEventType) t;
|
||||
eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, name, defaultValue, new ArrayList<>(2)));
|
||||
}
|
||||
for (Element fieldElement : typeElement.elements(ELEMENT_FIELD)) {
|
||||
String name = fieldElement.attribute(ATTRIBUTE_NAME);
|
||||
Type fieldType = getType(ATTRIBUTE_TYPE_ID, fieldElement);
|
||||
long dimension = fieldElement.attribute(ATTRIBUTE_DIMENSION, 0);
|
||||
boolean constantPool = fieldElement.attribute(ATTRIBUTE_CONSTANT_POOL) != null;
|
||||
// Add annotation later, because they may refer to undefined
|
||||
// types at this stage
|
||||
t.add(PrivateAccess.getInstance().newValueDescriptor(name, fieldType, new ArrayList<>(), (int) dimension, constantPool, null));
|
||||
}
|
||||
t.trimFields();
|
||||
}
|
||||
}
|
||||
|
||||
private void declareTypes(Element metadata) {
|
||||
for (Element typeElement : metadata.elements(ELEMENT_TYPE)) {
|
||||
String typeName = typeElement.attribute(ATTRIBUTE_NAME);
|
||||
String superType = typeElement.attribute(ATTRIBUTE_SUPER_TYPE);
|
||||
boolean simpleType = typeElement.attribute(ATTRIBUTE_SIMPLE_TYPE) != null;
|
||||
long id = typeElement.attribute(ATTRIBUTE_ID, -1);
|
||||
Type t;
|
||||
if (Type.SUPER_TYPE_EVENT.equals(superType)) {
|
||||
t = new PlatformEventType(typeName, id, false, false);
|
||||
} else {
|
||||
t = new Type(typeName, superType, id, false, simpleType);
|
||||
}
|
||||
types.put(id, t);
|
||||
descriptor.types.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
public MetadataDescriptor getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
277
jdkSrc/jdk8/jdk/jfr/internal/MetadataRepository.java
Normal file
277
jdkSrc/jdk8/jdk/jfr/internal/MetadataRepository.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static jdk.jfr.internal.LogLevel.DEBUG;
|
||||
import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.Period;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.Threshold;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.RequestEngine.RequestHook;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
|
||||
public final class MetadataRepository {
|
||||
|
||||
private static final JVM jvm = JVM.getJVM();
|
||||
private static final MetadataRepository instace = new MetadataRepository();
|
||||
|
||||
private final List<EventType> nativeEventTypes = new ArrayList<>(100);
|
||||
private final List<EventControl> nativeControls = new ArrayList<EventControl>(100);
|
||||
private final TypeLibrary typeLibrary = TypeLibrary.getInstance();
|
||||
private final SettingsManager settingsManager = new SettingsManager();
|
||||
private boolean staleMetadata = true;
|
||||
private boolean unregistered;
|
||||
private long lastUnloaded = -1;
|
||||
|
||||
public MetadataRepository() {
|
||||
initializeJVMEventTypes();
|
||||
}
|
||||
|
||||
private void initializeJVMEventTypes() {
|
||||
List<RequestHook> requestHooks = new ArrayList<>();
|
||||
for (Type type : typeLibrary.getTypes()) {
|
||||
if (type instanceof PlatformEventType) {
|
||||
PlatformEventType pEventType = (PlatformEventType) type;
|
||||
EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
|
||||
pEventType.setHasDuration(eventType.getAnnotation(Threshold.class) != null);
|
||||
pEventType.setHasStackTrace(eventType.getAnnotation(StackTrace.class) != null);
|
||||
pEventType.setHasCutoff(eventType.getAnnotation(Cutoff.class) != null);
|
||||
pEventType.setHasPeriod(eventType.getAnnotation(Period.class) != null);
|
||||
// Must add hook before EventControl is created as it removes
|
||||
// annotations, such as Period and Threshold.
|
||||
if (pEventType.hasPeriod()) {
|
||||
pEventType.setEventHook(true);
|
||||
if (!(Type.EVENT_NAME_PREFIX + "ExecutionSample").equals(type.getName())) {
|
||||
requestHooks.add(new RequestHook(pEventType));
|
||||
}
|
||||
}
|
||||
nativeControls.add(new EventControl(pEventType));
|
||||
nativeEventTypes.add(eventType);
|
||||
}
|
||||
}
|
||||
RequestEngine.addHooks(requestHooks);
|
||||
}
|
||||
|
||||
public static MetadataRepository getInstance() {
|
||||
return instace;
|
||||
}
|
||||
|
||||
public synchronized List<EventType> getRegisteredEventTypes() {
|
||||
List<EventHandler> handlers = getEventHandlers();
|
||||
List<EventType> eventTypes = new ArrayList<>(handlers.size() + nativeEventTypes.size());
|
||||
for (EventHandler h : handlers) {
|
||||
if (h.isRegistered()) {
|
||||
eventTypes.add(h.getEventType());
|
||||
}
|
||||
}
|
||||
eventTypes.addAll(nativeEventTypes);
|
||||
return eventTypes;
|
||||
}
|
||||
|
||||
public synchronized EventType getEventType(Class<? extends Event> eventClass) {
|
||||
EventHandler h = getHandler(eventClass);
|
||||
if (h != null && h.isRegistered()) {
|
||||
return h.getEventType();
|
||||
}
|
||||
throw new IllegalStateException("Event class " + eventClass.getName() + " is not registered");
|
||||
}
|
||||
|
||||
public synchronized void unregister(Class<? extends Event> eventClass) {
|
||||
Utils.checkRegisterPermission();
|
||||
EventHandler handler = getHandler(eventClass);
|
||||
if (handler != null) {
|
||||
handler.setRegistered(false);
|
||||
}
|
||||
// never registered, ignore call
|
||||
}
|
||||
public synchronized EventType register(Class<? extends Event> eventClass) {
|
||||
return register(eventClass, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public synchronized EventType register(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
|
||||
Utils.checkRegisterPermission();
|
||||
EventHandler handler = getHandler(eventClass);
|
||||
if (handler == null) {
|
||||
handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields);
|
||||
}
|
||||
handler.setRegistered(true);
|
||||
typeLibrary.addType(handler.getPlatformEventType());
|
||||
if (jvm.isRecording()) {
|
||||
storeDescriptorInJVM(); // needed for emergency dump
|
||||
settingsManager.setEventControl(handler.getEventControl());
|
||||
settingsManager.updateRetransform(Collections.singletonList((eventClass)));
|
||||
} else {
|
||||
setStaleMetadata();
|
||||
}
|
||||
return handler.getEventType();
|
||||
}
|
||||
|
||||
private EventHandler getHandler(Class<? extends Event> eventClass) {
|
||||
Utils.ensureValidEventSubclass(eventClass);
|
||||
SecuritySupport.makeVisibleToJFR(eventClass);
|
||||
Utils.ensureInitialized(eventClass);
|
||||
return Utils.getHandler(eventClass);
|
||||
}
|
||||
|
||||
private EventHandler makeHandler(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) throws InternalError {
|
||||
SecuritySupport.addHandlerExport(eventClass);
|
||||
PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields);
|
||||
EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
|
||||
EventControl ec = new EventControl(pEventType, eventClass);
|
||||
Class<? extends EventHandler> handlerClass = null;
|
||||
try {
|
||||
String eventHandlerName = EventHandlerCreator.makeEventHandlerName(eventType.getId());
|
||||
handlerClass = Class.forName(eventHandlerName, false, Event.class.getClassLoader()).asSubclass(EventHandler.class);
|
||||
// Created eagerly on class load, tag as instrumented
|
||||
pEventType.setInstrumented();
|
||||
Logger.log(JFR_SYSTEM, DEBUG, "Found existing event handler for " + eventType.getName());
|
||||
} catch (ClassNotFoundException cne) {
|
||||
EventHandlerCreator ehc = new EventHandlerCreator(eventType.getId(), ec.getSettingInfos(), eventType, eventClass);
|
||||
handlerClass = ehc.makeEventHandlerClass();
|
||||
Logger.log(LogTag.JFR_SYSTEM, DEBUG, "Created event handler for " + eventType.getName());
|
||||
}
|
||||
EventHandler handler = EventHandlerCreator.instantiateEventHandler(handlerClass, true, eventType, ec);
|
||||
Utils.setHandler(eventClass, handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
public synchronized void setSettings(List<Map<String, String>> list) {
|
||||
settingsManager.setSettings(list);
|
||||
}
|
||||
|
||||
synchronized void disableEvents() {
|
||||
for (EventControl c : getEventControls()) {
|
||||
c.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized List<EventControl> getEventControls() {
|
||||
List<EventControl> controls = new ArrayList<>();
|
||||
controls.addAll(nativeControls);
|
||||
for (EventHandler eh : getEventHandlers()) {
|
||||
controls.add(eh.getEventControl());
|
||||
}
|
||||
return controls;
|
||||
}
|
||||
|
||||
private void storeDescriptorInJVM() throws InternalError {
|
||||
jvm.storeMetadataDescriptor(getBinaryRepresentation());
|
||||
staleMetadata = false;
|
||||
}
|
||||
|
||||
private static List<EventHandler> getEventHandlers() {
|
||||
List<Class<? extends Event>> allEventClasses = jvm.getAllEventClasses();
|
||||
List<EventHandler> eventHandlers = new ArrayList<>(allEventClasses.size());
|
||||
for (Class<? extends Event> clazz : allEventClasses) {
|
||||
EventHandler eh = Utils.getHandler(clazz);
|
||||
if (eh != null) {
|
||||
eventHandlers.add(eh);
|
||||
}
|
||||
}
|
||||
return eventHandlers;
|
||||
}
|
||||
|
||||
private byte[] getBinaryRepresentation() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(40000);
|
||||
DataOutputStream daos = new DataOutputStream(baos);
|
||||
try {
|
||||
List<Type> types = typeLibrary.getTypes();
|
||||
Collections.sort(types);
|
||||
MetadataDescriptor.write(types, daos);
|
||||
daos.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
// should not happen
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized boolean isEnabled(String eventName) {
|
||||
return settingsManager.isEnabled(eventName);
|
||||
}
|
||||
|
||||
synchronized void setStaleMetadata() {
|
||||
staleMetadata = true;
|
||||
}
|
||||
|
||||
// Lock around setOutput ensures that other threads dosn't
|
||||
// emit event after setOutput and unregister the event class, before a call
|
||||
// to storeDescriptorInJVM
|
||||
synchronized void setOutput(String filename) {
|
||||
jvm.setOutput(filename);
|
||||
|
||||
unregisterUnloaded();
|
||||
if (unregistered) {
|
||||
staleMetadata = typeLibrary.clearUnregistered();
|
||||
unregistered = false;
|
||||
}
|
||||
if (staleMetadata) {
|
||||
storeDescriptorInJVM();
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterUnloaded() {
|
||||
long unloaded = jvm.getUnloadedEventClassCount();
|
||||
if (this.lastUnloaded != unloaded) {
|
||||
this.lastUnloaded = unloaded;
|
||||
List<Class<? extends Event>> eventClasses = jvm.getAllEventClasses();
|
||||
HashSet<Long> knownIds = new HashSet<>(eventClasses.size());
|
||||
for (Class<? extends Event> ec: eventClasses) {
|
||||
knownIds.add(Type.getTypeId(ec));
|
||||
}
|
||||
for (Type type : typeLibrary.getTypes()) {
|
||||
if (type instanceof PlatformEventType) {
|
||||
if (!knownIds.contains(type.getId())) {
|
||||
PlatformEventType pe = (PlatformEventType) type;
|
||||
if (!pe.isJVM()) {
|
||||
pe.setRegistered(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void setUnregistered() {
|
||||
unregistered = true;
|
||||
}
|
||||
|
||||
}
|
||||
225
jdkSrc/jdk8/jdk/jfr/internal/MetadataWriter.java
Normal file
225
jdkSrc/jdk8/jdk/jfr/internal/MetadataWriter.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_CONSTANT_POOL;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DEFAULT_VALUE;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DIMENSION;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_GMT_OFFSET;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_ID;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_LOCALE;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_NAME;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SIMPLE_TYPE;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SUPER_TYPE;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_TYPE_ID;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_ANNOTATION;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_FIELD;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_SETTING;
|
||||
import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_TYPE;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.MetadataDescriptor.Attribute;
|
||||
import jdk.jfr.internal.MetadataDescriptor.Element;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
|
||||
/**
|
||||
* Class responsible for converting a list of types into a format that can be
|
||||
* parsed by a client.
|
||||
*
|
||||
*/
|
||||
final class MetadataWriter {
|
||||
|
||||
private final Element metadata = new Element("metadata");
|
||||
private final Element root = new Element("root");
|
||||
|
||||
public MetadataWriter(MetadataDescriptor descriptor) {
|
||||
descriptor.getTypes().forEach(type -> makeTypeElement(metadata, type));
|
||||
|
||||
root.add(metadata);
|
||||
Element region = new Element("region");
|
||||
region.addAttribute(ATTRIBUTE_LOCALE, descriptor.locale);
|
||||
region.addAttribute(ATTRIBUTE_GMT_OFFSET, descriptor.gmtOffset);
|
||||
root.add(region);
|
||||
}
|
||||
|
||||
public void writeBinary(DataOutput output) throws IOException {
|
||||
Set<String> stringPool = new HashSet<>(1000);
|
||||
// Possible improvement, sort string by how often they occur.
|
||||
// and assign low number to the most frequently used.
|
||||
buildStringPool(root, stringPool);
|
||||
HashMap<String, Integer> lookup = new LinkedHashMap<>(stringPool.size());
|
||||
int index = 0;
|
||||
int poolSize = stringPool.size();
|
||||
writeInt(output, poolSize);
|
||||
for (String s : stringPool) {
|
||||
lookup.put(s, index);
|
||||
writeString(output, s);
|
||||
index++;
|
||||
}
|
||||
write(output, root, lookup);
|
||||
}
|
||||
|
||||
private void writeString(DataOutput out, String s) throws IOException {
|
||||
if (s == null ) {
|
||||
out.writeByte(RecordingInput.STRING_ENCODING_NULL);
|
||||
return;
|
||||
}
|
||||
out.writeByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16
|
||||
int length = s.length();
|
||||
writeInt(out, length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
writeInt(out, s.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInt(DataOutput out, int v) throws IOException {
|
||||
|
||||
long s = v & 0xffffffffL;
|
||||
if (s < 1 << 7) {
|
||||
out.write((byte) (s));
|
||||
return;
|
||||
}
|
||||
out.write((byte) (s | 0x80)); // first byte written
|
||||
s >>= 7;
|
||||
if (s < 1 << 7) {
|
||||
out.write((byte) (s));
|
||||
return;
|
||||
}
|
||||
out.write((byte) (s | 0x80)); // second byte written
|
||||
s >>= 7;
|
||||
if (s < 1 << 7) {
|
||||
out.write((byte) (s));
|
||||
return;
|
||||
}
|
||||
out.write((byte) (s | 0x80)); // third byte written
|
||||
s >>= 7;
|
||||
if (s < 1 << 7) {
|
||||
out.write((byte) (s));
|
||||
return;
|
||||
}
|
||||
s >>= 7;
|
||||
out.write((byte) (s));// fourth byte written
|
||||
}
|
||||
|
||||
private void buildStringPool(Element element, Set<String> pool) {
|
||||
pool.add(element.name);
|
||||
for (Attribute a : element.attributes) {
|
||||
pool.add(a.name);
|
||||
pool.add(a.value);
|
||||
}
|
||||
for (Element child : element.elements) {
|
||||
buildStringPool(child, pool);
|
||||
}
|
||||
}
|
||||
|
||||
private void write(DataOutput output,Element element, HashMap<String, Integer> lookup) throws IOException {
|
||||
writeInt(output, lookup.get(element.name));
|
||||
writeInt(output, element.attributes.size());
|
||||
for (Attribute a : element.attributes) {
|
||||
writeInt(output, lookup.get(a.name));
|
||||
writeInt(output, lookup.get(a.value));
|
||||
}
|
||||
writeInt(output, element.elements.size());
|
||||
for (Element child : element.elements) {
|
||||
write(output, child, lookup);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeTypeElement(Element root, Type type) {
|
||||
Element element = root.newChild(ELEMENT_TYPE);
|
||||
element.addAttribute(ATTRIBUTE_NAME, type.getName());
|
||||
String superType = type.getSuperType();
|
||||
if (superType != null) {
|
||||
element.addAttribute(ATTRIBUTE_SUPER_TYPE, superType);
|
||||
}
|
||||
if (type.isSimpleType()) {
|
||||
element.addAttribute(ATTRIBUTE_SIMPLE_TYPE, true);
|
||||
}
|
||||
element.addAttribute(ATTRIBUTE_ID, type.getId());
|
||||
if (type instanceof PlatformEventType) {
|
||||
for (SettingDescriptor v : ((PlatformEventType)type).getSettings()) {
|
||||
makeSettingElement(element, v);
|
||||
}
|
||||
}
|
||||
for (ValueDescriptor v : type.getFields()) {
|
||||
makeFieldElement(element, v);
|
||||
}
|
||||
for (AnnotationElement a : type.getAnnotationElements()) {
|
||||
makeAnnotation(element, a);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeSettingElement(Element typeElement, SettingDescriptor s) {
|
||||
Element element = typeElement.newChild(ELEMENT_SETTING);
|
||||
element.addAttribute(ATTRIBUTE_NAME, s.getName());
|
||||
element.addAttribute(ATTRIBUTE_TYPE_ID, s.getTypeId());
|
||||
element.addAttribute(ATTRIBUTE_DEFAULT_VALUE, s.getDefaultValue());
|
||||
for (AnnotationElement a : s.getAnnotationElements()) {
|
||||
makeAnnotation(element, a);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeFieldElement(Element typeElement, ValueDescriptor v) {
|
||||
Element element = typeElement.newChild(ELEMENT_FIELD);
|
||||
element.addAttribute(ATTRIBUTE_NAME, v.getName());
|
||||
element.addAttribute(ATTRIBUTE_TYPE_ID, v.getTypeId());
|
||||
if (v.isArray()) {
|
||||
element.addAttribute(ATTRIBUTE_DIMENSION, 1);
|
||||
}
|
||||
if (PrivateAccess.getInstance().isConstantPool(v)) {
|
||||
element.addAttribute(ATTRIBUTE_CONSTANT_POOL, true);
|
||||
}
|
||||
for (AnnotationElement a : v.getAnnotationElements()) {
|
||||
makeAnnotation(element, a);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeAnnotation(Element entity, AnnotationElement annotation) {
|
||||
Element element = entity.newChild(ELEMENT_ANNOTATION);
|
||||
element.addAttribute(ATTRIBUTE_TYPE_ID, annotation.getTypeId());
|
||||
List<Object> values = annotation.getValues();
|
||||
int index = 0;
|
||||
for (ValueDescriptor v : annotation.getValueDescriptors()) {
|
||||
Object value = values.get(index++);
|
||||
if (v.isArray()) {
|
||||
element.addArrayAttribute(element, v.getName(), value);
|
||||
} else {
|
||||
element.addAttribute(v.getName(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
98
jdkSrc/jdk8/jdk/jfr/internal/OldObjectSample.java
Normal file
98
jdkSrc/jdk8/jdk/jfr/internal/OldObjectSample.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.jfr.Enabled;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.settings.CutoffSetting;
|
||||
import jdk.jfr.internal.test.WhiteBox;
|
||||
|
||||
// The Old Object event could have been implemented as a periodic event, but
|
||||
// due to chunk rotations and how settings are calculated when multiple recordings
|
||||
// are running at the same time, it would lead to unacceptable overhead.
|
||||
//
|
||||
// Instead, the event is only emitted before a recording stops and
|
||||
// if that recording has the event enabled.
|
||||
//
|
||||
// This requires special handling and the purpose of this class is to provide that
|
||||
//
|
||||
public final class OldObjectSample {
|
||||
|
||||
private static final String EVENT_NAME = Type.EVENT_NAME_PREFIX + "OldObjectSample";
|
||||
private static final String OLD_OBJECT_CUTOFF = EVENT_NAME + "#" + Cutoff.NAME;
|
||||
private static final String OLD_OBJECT_ENABLED = EVENT_NAME + "#" + Enabled.NAME;
|
||||
|
||||
// Emit if old object is enabled in recording with cutoff for that recording
|
||||
public static void emit(PlatformRecording recording) {
|
||||
if (isEnabled(recording)) {
|
||||
long nanos = CutoffSetting.parseValueSafe(recording.getSettings().get(OLD_OBJECT_CUTOFF));
|
||||
long ticks = Utils.nanosToTicks(nanos);
|
||||
JVM.getJVM().emitOldObjectSamples(ticks, WhiteBox.getWriteAllObjectSamples());
|
||||
}
|
||||
}
|
||||
|
||||
// Emit if old object is enabled for at least one recording, and use the largest
|
||||
// cutoff for an enabled recording
|
||||
public static void emit(List<PlatformRecording> recordings, Boolean pathToGcRoots) {
|
||||
boolean enabled = false;
|
||||
long cutoffNanos = Boolean.TRUE.equals(pathToGcRoots) ? Long.MAX_VALUE : 0L;
|
||||
for (PlatformRecording r : recordings) {
|
||||
if (r.getState() == RecordingState.RUNNING) {
|
||||
if (isEnabled(r)) {
|
||||
enabled = true;
|
||||
long c = CutoffSetting.parseValueSafe(r.getSettings().get(OLD_OBJECT_CUTOFF));
|
||||
cutoffNanos = Math.max(c, cutoffNanos);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
long ticks = Utils.nanosToTicks(cutoffNanos);
|
||||
JVM.getJVM().emitOldObjectSamples(ticks, WhiteBox.getWriteAllObjectSamples());
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateSettingPathToGcRoots(Map<String, String> s, Boolean pathToGcRoots) {
|
||||
if (pathToGcRoots != null) {
|
||||
s.put(OLD_OBJECT_CUTOFF, pathToGcRoots ? "infinity" : "0 ns");
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> createSettingsForSnapshot(PlatformRecording recording, Boolean pathToGcRoots) {
|
||||
Map<String, String> settings = new HashMap<>(recording.getSettings());
|
||||
updateSettingPathToGcRoots(settings, pathToGcRoots);
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static boolean isEnabled(PlatformRecording r) {
|
||||
Map<String, String> settings = r.getSettings();
|
||||
String s = settings.get(OLD_OBJECT_ENABLED);
|
||||
return "true".equals(s);
|
||||
}
|
||||
}
|
||||
162
jdkSrc/jdk8/jdk/jfr/internal/Options.java
Normal file
162
jdkSrc/jdk8/jdk/jfr/internal/Options.java
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* Options that control Flight Recorder.
|
||||
*
|
||||
* Can be set using JFR.configure
|
||||
*
|
||||
*/
|
||||
public final class Options {
|
||||
|
||||
private final static JVM jvm = JVM.getJVM();
|
||||
private final static long WAIT_INTERVAL = 1000; // ms;
|
||||
|
||||
private final static long MIN_MAX_CHUNKSIZE = 1024 * 1024;
|
||||
|
||||
private static final long DEFAULT_GLOBAL_BUFFER_COUNT = 20;
|
||||
private static final long DEFAULT_GLOBAL_BUFFER_SIZE = 524288;
|
||||
private static final long DEFAULT_MEMORY_SIZE = DEFAULT_GLOBAL_BUFFER_COUNT * DEFAULT_GLOBAL_BUFFER_SIZE;
|
||||
private static long DEFAULT_THREAD_BUFFER_SIZE;
|
||||
private static final int DEFAULT_STACK_DEPTH = 64;
|
||||
private static final boolean DEFAULT_SAMPLE_THREADS = true;
|
||||
private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024;
|
||||
private static final SafePath DEFAULT_DUMP_PATH = SecuritySupport.USER_HOME;
|
||||
|
||||
private static long memorySize;
|
||||
private static long globalBufferSize;
|
||||
private static long globalBufferCount;
|
||||
private static long threadBufferSize;
|
||||
private static int stackDepth;
|
||||
private static boolean sampleThreads;
|
||||
private static long maxChunkSize;
|
||||
private static SafePath dumpPath;
|
||||
|
||||
static {
|
||||
final long pageSize = Unsafe.getUnsafe().pageSize();
|
||||
DEFAULT_THREAD_BUFFER_SIZE = pageSize > 8 * 1024 ? pageSize : 8 * 1024;
|
||||
reset();
|
||||
}
|
||||
|
||||
public static synchronized void setMaxChunkSize(long max) {
|
||||
if (max < MIN_MAX_CHUNKSIZE) {
|
||||
throw new IllegalArgumentException("Max chunk size must be at least " + MIN_MAX_CHUNKSIZE);
|
||||
}
|
||||
jvm.setFileNotification(max);
|
||||
maxChunkSize = max;
|
||||
}
|
||||
|
||||
public static synchronized long getMaxChunkSize() {
|
||||
return maxChunkSize;
|
||||
}
|
||||
|
||||
public static synchronized void setMemorySize(long memSize) {
|
||||
jvm.setMemorySize(memSize);
|
||||
memorySize = memSize;
|
||||
}
|
||||
|
||||
public static synchronized long getMemorySize() {
|
||||
return memorySize;
|
||||
}
|
||||
|
||||
public static synchronized void setThreadBufferSize(long threadBufSize) {
|
||||
jvm.setThreadBufferSize(threadBufSize);
|
||||
threadBufferSize = threadBufSize;
|
||||
}
|
||||
|
||||
public static synchronized long getThreadBufferSize() {
|
||||
return threadBufferSize;
|
||||
}
|
||||
|
||||
public static synchronized long getGlobalBufferSize() {
|
||||
return globalBufferSize;
|
||||
}
|
||||
|
||||
public static synchronized void setGlobalBufferCount(long globalBufCount) {
|
||||
jvm.setGlobalBufferCount(globalBufCount);
|
||||
globalBufferCount = globalBufCount;
|
||||
}
|
||||
|
||||
public static synchronized long getGlobalBufferCount() {
|
||||
return globalBufferCount;
|
||||
}
|
||||
|
||||
public static synchronized void setGlobalBufferSize(long globalBufsize) {
|
||||
jvm.setGlobalBufferSize(globalBufsize);
|
||||
globalBufferSize = globalBufsize;
|
||||
}
|
||||
|
||||
public static synchronized void setDumpPath(SafePath path) {
|
||||
dumpPath = path;
|
||||
}
|
||||
|
||||
public static synchronized SafePath getDumpPath() {
|
||||
return dumpPath;
|
||||
}
|
||||
|
||||
public static synchronized void setStackDepth(Integer stackTraceDepth) {
|
||||
jvm.setStackDepth(stackTraceDepth);
|
||||
stackDepth = stackTraceDepth;
|
||||
}
|
||||
|
||||
public static synchronized int getStackDepth() {
|
||||
return stackDepth;
|
||||
}
|
||||
|
||||
public static synchronized void setSampleThreads(Boolean sample) {
|
||||
jvm.setSampleThreads(sample);
|
||||
sampleThreads = sample;
|
||||
}
|
||||
|
||||
public static synchronized boolean getSampleThreads() {
|
||||
return sampleThreads;
|
||||
}
|
||||
|
||||
private static synchronized void reset() {
|
||||
setMaxChunkSize(DEFAULT_MAX_CHUNK_SIZE);
|
||||
setMemorySize(DEFAULT_MEMORY_SIZE);
|
||||
setGlobalBufferSize(DEFAULT_GLOBAL_BUFFER_SIZE);
|
||||
setGlobalBufferCount(DEFAULT_GLOBAL_BUFFER_COUNT);
|
||||
setDumpPath(DEFAULT_DUMP_PATH);
|
||||
setSampleThreads(DEFAULT_SAMPLE_THREADS);
|
||||
setStackDepth(DEFAULT_STACK_DEPTH);
|
||||
setThreadBufferSize(DEFAULT_THREAD_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
static synchronized long getWaitInterval() {
|
||||
return WAIT_INTERVAL;
|
||||
}
|
||||
|
||||
static void ensureInitialized() {
|
||||
// trigger clinit which will setup JVM defaults.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
281
jdkSrc/jdk8/jdk/jfr/internal/PlatformEventType.java
Normal file
281
jdkSrc/jdk8/jdk/jfr/internal/PlatformEventType.java
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
|
||||
/**
|
||||
* Implementation of event type.
|
||||
*
|
||||
* To avoid memory leaks, this class must not hold strong reference to an event
|
||||
* class or a setting class
|
||||
*/
|
||||
public final class PlatformEventType extends Type {
|
||||
private final boolean isJVM;
|
||||
private final boolean isJDK;
|
||||
private final boolean isMethodSampling;
|
||||
private final List<SettingDescriptor> settings = new ArrayList<>(5);
|
||||
private final boolean dynamicSettings;
|
||||
private final int stackTraceOffset;
|
||||
|
||||
// default values
|
||||
private boolean enabled = false;
|
||||
private boolean stackTraceEnabled = true;
|
||||
private long thresholdTicks = 0;
|
||||
private long period = 0;
|
||||
private boolean hasHook;
|
||||
|
||||
private boolean beginChunk;
|
||||
private boolean endChunk;
|
||||
private boolean hasStackTrace = true;
|
||||
private boolean hasDuration = true;
|
||||
private boolean hasPeriod = true;
|
||||
private boolean hasCutoff = false;
|
||||
private boolean isInstrumented;
|
||||
private boolean markForInstrumentation;
|
||||
private boolean registered = true;
|
||||
private boolean commitable = enabled && registered;
|
||||
|
||||
|
||||
// package private
|
||||
PlatformEventType(String name, long id, boolean isJDK, boolean dynamicSettings) {
|
||||
super(name, Type.SUPER_TYPE_EVENT, id);
|
||||
this.dynamicSettings = dynamicSettings;
|
||||
this.isJVM = Type.isDefinedByJVM(id);
|
||||
this.isMethodSampling = name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample");
|
||||
this.isJDK = isJDK;
|
||||
this.stackTraceOffset = stackTraceOffset(name, isJDK);
|
||||
}
|
||||
|
||||
private static int stackTraceOffset(String name, boolean isJDK) {
|
||||
if (isJDK) {
|
||||
if (name.equals(Type.EVENT_NAME_PREFIX + "JavaExceptionThrow")) {
|
||||
return 5;
|
||||
}
|
||||
if (name.equals(Type.EVENT_NAME_PREFIX + "JavaErrorThrow")) {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
public void add(SettingDescriptor settingDescriptor) {
|
||||
Objects.requireNonNull(settingDescriptor);
|
||||
settings.add(settingDescriptor);
|
||||
}
|
||||
|
||||
public List<SettingDescriptor> getSettings() {
|
||||
if (dynamicSettings) {
|
||||
List<SettingDescriptor> list = new ArrayList<>(settings.size());
|
||||
for (SettingDescriptor s : settings) {
|
||||
if (Utils.isSettingVisible(s.getTypeId(), hasHook)) {
|
||||
list.add(s);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
public List<SettingDescriptor> getAllSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public void setHasStackTrace(boolean hasStackTrace) {
|
||||
this.hasStackTrace = hasStackTrace;
|
||||
}
|
||||
|
||||
public void setHasDuration(boolean hasDuration) {
|
||||
this.hasDuration = hasDuration;
|
||||
}
|
||||
|
||||
public void setHasCutoff(boolean hasCutoff) {
|
||||
this.hasCutoff = hasCutoff;
|
||||
}
|
||||
|
||||
public void setCutoff(long cutoffNanos) {
|
||||
if (isJVM) {
|
||||
long cutoffTicks = Utils.nanosToTicks(cutoffNanos);
|
||||
JVM.getJVM().setCutoff(getId(), cutoffTicks);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHasPeriod(boolean hasPeriod) {
|
||||
this.hasPeriod = hasPeriod;
|
||||
}
|
||||
|
||||
public boolean hasStackTrace() {
|
||||
return this.hasStackTrace;
|
||||
}
|
||||
|
||||
public boolean hasDuration() {
|
||||
return this.hasDuration;
|
||||
}
|
||||
|
||||
public boolean hasPeriod() {
|
||||
return this.hasPeriod;
|
||||
}
|
||||
|
||||
public boolean hasCutoff() {
|
||||
return this.hasCutoff;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public boolean isJVM() {
|
||||
return isJVM;
|
||||
}
|
||||
|
||||
public boolean isJDK() {
|
||||
return isJDK;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
updateCommitable();
|
||||
if (isJVM) {
|
||||
if (isMethodSampling) {
|
||||
long p = enabled ? period : 0;
|
||||
JVM.getJVM().setMethodSamplingInterval(getId(), p);
|
||||
} else {
|
||||
JVM.getJVM().setEnabled(getId(), enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPeriod(long periodMillis, boolean beginChunk, boolean endChunk) {
|
||||
if (isMethodSampling) {
|
||||
long p = enabled ? periodMillis : 0;
|
||||
JVM.getJVM().setMethodSamplingInterval(getId(), p);
|
||||
}
|
||||
this.beginChunk = beginChunk;
|
||||
this.endChunk = endChunk;
|
||||
this.period = periodMillis;
|
||||
}
|
||||
|
||||
public void setStackTraceEnabled(boolean stackTraceEnabled) {
|
||||
this.stackTraceEnabled = stackTraceEnabled;
|
||||
if (isJVM) {
|
||||
JVM.getJVM().setStackTraceEnabled(getId(), stackTraceEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
public void setThreshold(long thresholdNanos) {
|
||||
this.thresholdTicks = Utils.nanosToTicks(thresholdNanos);
|
||||
if (isJVM) {
|
||||
JVM.getJVM().setThreshold(getId(), thresholdTicks);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEveryChunk() {
|
||||
return period == 0;
|
||||
}
|
||||
|
||||
public boolean getStackTraceEnabled() {
|
||||
return stackTraceEnabled;
|
||||
}
|
||||
|
||||
public long getThresholdTicks() {
|
||||
return thresholdTicks;
|
||||
}
|
||||
|
||||
public long getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
public boolean hasEventHook() {
|
||||
return hasHook;
|
||||
}
|
||||
|
||||
public void setEventHook(boolean hasHook) {
|
||||
this.hasHook = hasHook;
|
||||
}
|
||||
|
||||
public boolean isBeginChunk() {
|
||||
return beginChunk;
|
||||
}
|
||||
|
||||
public boolean isEndChunk() {
|
||||
return endChunk;
|
||||
}
|
||||
|
||||
public boolean isInstrumented() {
|
||||
return isInstrumented;
|
||||
}
|
||||
|
||||
public void setInstrumented() {
|
||||
isInstrumented = true;
|
||||
}
|
||||
|
||||
public void markForInstrumentation(boolean markForInstrumentation) {
|
||||
this.markForInstrumentation = markForInstrumentation;
|
||||
}
|
||||
|
||||
public boolean isMarkedForInstrumentation() {
|
||||
return markForInstrumentation;
|
||||
}
|
||||
|
||||
public boolean setRegistered(boolean registered) {
|
||||
if (this.registered != registered) {
|
||||
this.registered = registered;
|
||||
updateCommitable();
|
||||
LogTag logTag = isJVM() || isJDK() ? LogTag.JFR_SYSTEM_EVENT : LogTag.JFR_EVENT;
|
||||
if (registered) {
|
||||
Logger.log(logTag, LogLevel.INFO, "Registered " + getLogName());
|
||||
} else {
|
||||
Logger.log(logTag, LogLevel.INFO, "Unregistered " + getLogName());
|
||||
}
|
||||
if (!registered) {
|
||||
MetadataRepository.getInstance().setUnregistered();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateCommitable() {
|
||||
this.commitable = enabled && registered;
|
||||
}
|
||||
|
||||
public final boolean isRegistered() {
|
||||
return registered;
|
||||
}
|
||||
|
||||
// Efficient check of enabled && registered
|
||||
public boolean isCommitable() {
|
||||
return commitable;
|
||||
}
|
||||
|
||||
public int getStackTraceOffset() {
|
||||
return stackTraceOffset;
|
||||
}
|
||||
}
|
||||
554
jdkSrc/jdk8/jdk/jfr/internal/PlatformRecorder.java
Normal file
554
jdkSrc/jdk8/jdk/jfr/internal/PlatformRecorder.java
Normal file
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static jdk.jfr.internal.LogLevel.INFO;
|
||||
import static jdk.jfr.internal.LogLevel.TRACE;
|
||||
import static jdk.jfr.internal.LogLevel.WARN;
|
||||
import static jdk.jfr.internal.LogTag.JFR;
|
||||
import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
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.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.events.ActiveRecordingEvent;
|
||||
import jdk.jfr.events.ActiveSettingEvent;
|
||||
import jdk.jfr.internal.SecuritySupport.SecureRecorderListener;
|
||||
import jdk.jfr.internal.instrument.JDKEvents;
|
||||
|
||||
public final class PlatformRecorder {
|
||||
|
||||
private final List<PlatformRecording> recordings = new ArrayList<>();
|
||||
private final static List<SecureRecorderListener> changeListeners = new ArrayList<>();
|
||||
private final Repository repository;
|
||||
private final Timer timer;
|
||||
private final static JVM jvm = JVM.getJVM();
|
||||
private final EventType activeRecordingEvent;
|
||||
private final EventType activeSettingEvent;
|
||||
private final Thread shutdownHook;
|
||||
|
||||
private long recordingCounter = 0;
|
||||
private RepositoryChunk currentChunk;
|
||||
|
||||
public PlatformRecorder() throws Exception {
|
||||
repository = Repository.getRepository();
|
||||
Logger.log(JFR_SYSTEM, INFO, "Initialized disk repository");
|
||||
repository.ensureRepository();
|
||||
jvm.createNativeJFR();
|
||||
Logger.log(JFR_SYSTEM, INFO, "Created native");
|
||||
JDKEvents.initialize();
|
||||
Logger.log(JFR_SYSTEM, INFO, "Registered JDK events");
|
||||
JDKEvents.addInstrumentation();
|
||||
startDiskMonitor();
|
||||
SecuritySupport.registerEvent(ActiveRecordingEvent.class);
|
||||
activeRecordingEvent = EventType.getEventType(ActiveRecordingEvent.class);
|
||||
SecuritySupport.registerEvent(ActiveSettingEvent.class);
|
||||
activeSettingEvent = EventType.getEventType(ActiveSettingEvent.class);
|
||||
shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR: Shutdown Hook", new ShutdownHook(this));
|
||||
SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler());
|
||||
SecuritySupport.registerShutdownHook(shutdownHook);
|
||||
timer = createTimer();
|
||||
}
|
||||
|
||||
private static Timer createTimer() {
|
||||
try {
|
||||
List<Timer> result = new CopyOnWriteArrayList<>();
|
||||
Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
|
||||
result.add(new Timer("JFR Recording Scheduler", true));
|
||||
});
|
||||
t.start();
|
||||
t.join();
|
||||
return result.get(0);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException("Not able to create timer task. " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized PlatformRecording newRecording(Map<String, String> settings) {
|
||||
return newRecording(settings, ++recordingCounter);
|
||||
}
|
||||
|
||||
// To be used internally when doing dumps.
|
||||
// Caller must have recorder lock and close recording before releasing lock
|
||||
public PlatformRecording newTemporaryRecording() {
|
||||
if(!Thread.holdsLock(this)) {
|
||||
throw new InternalError("Caller must have recorder lock");
|
||||
}
|
||||
return newRecording(new HashMap<>(), 0);
|
||||
}
|
||||
|
||||
private synchronized PlatformRecording newRecording(Map<String, String> settings, long id) {
|
||||
PlatformRecording recording = new PlatformRecording(this, id);
|
||||
if (!settings.isEmpty()) {
|
||||
recording.setSettings(settings);
|
||||
}
|
||||
recordings.add(recording);
|
||||
return recording;
|
||||
}
|
||||
|
||||
synchronized void finish(PlatformRecording recording) {
|
||||
if (recording.getState() == RecordingState.RUNNING) {
|
||||
recording.stop("Recording closed");
|
||||
}
|
||||
recordings.remove(recording);
|
||||
}
|
||||
|
||||
public synchronized List<PlatformRecording> getRecordings() {
|
||||
return Collections.unmodifiableList(new ArrayList<PlatformRecording>(recordings));
|
||||
}
|
||||
|
||||
public synchronized static void addListener(FlightRecorderListener changeListener) {
|
||||
AccessControlContext context = AccessController.getContext();
|
||||
SecureRecorderListener sl = new SecureRecorderListener(context, changeListener);
|
||||
boolean runInitialized;
|
||||
synchronized (PlatformRecorder.class) {
|
||||
runInitialized = FlightRecorder.isInitialized();
|
||||
changeListeners.add(sl);
|
||||
}
|
||||
if (runInitialized) {
|
||||
sl.recorderInitialized(FlightRecorder.getFlightRecorder());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static boolean removeListener(FlightRecorderListener changeListener) {
|
||||
for (SecureRecorderListener s : new ArrayList<>(changeListeners)) {
|
||||
if (s.getChangeListener() == changeListener) {
|
||||
changeListeners.remove(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static synchronized List<FlightRecorderListener> getListeners() {
|
||||
return new ArrayList<>(changeListeners);
|
||||
}
|
||||
|
||||
Timer getTimer() {
|
||||
return timer;
|
||||
}
|
||||
|
||||
public static void notifyRecorderInitialized(FlightRecorder recorder) {
|
||||
Logger.log(JFR_SYSTEM, TRACE, "Notifying listeners that Flight Recorder is initialized");
|
||||
for (FlightRecorderListener r : getListeners()) {
|
||||
r.recorderInitialized(recorder);
|
||||
}
|
||||
}
|
||||
|
||||
// called by shutdown hook
|
||||
synchronized void destroy() {
|
||||
try {
|
||||
timer.cancel();
|
||||
} catch (Exception ex) {
|
||||
Logger.log(JFR_SYSTEM, WARN, "Shutdown hook could not cancel timer");
|
||||
}
|
||||
|
||||
for (PlatformRecording p : getRecordings()) {
|
||||
if (p.getState() == RecordingState.RUNNING) {
|
||||
try {
|
||||
p.stop("Shutdown");
|
||||
} catch (Exception ex) {
|
||||
Logger.log(JFR, WARN, "Recording " + p.getName() + ":" + p.getId() + " could not be stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JDKEvents.remove();
|
||||
|
||||
if (jvm.hasNativeJFR()) {
|
||||
if (jvm.isRecording()) {
|
||||
jvm.endRecording_();
|
||||
}
|
||||
jvm.destroyNativeJFR();
|
||||
}
|
||||
repository.clear();
|
||||
}
|
||||
|
||||
synchronized void start(PlatformRecording recording) {
|
||||
// State can only be NEW or DELAYED because of previous checks
|
||||
Instant now = Instant.now();
|
||||
recording.setStartTime(now);
|
||||
recording.updateTimer();
|
||||
Duration duration = recording.getDuration();
|
||||
if (duration != null) {
|
||||
recording.setStopTime(now.plus(duration));
|
||||
}
|
||||
boolean toDisk = recording.isToDisk();
|
||||
boolean beginPhysical = true;
|
||||
for (PlatformRecording s : getRecordings()) {
|
||||
if (s.getState() == RecordingState.RUNNING) {
|
||||
beginPhysical = false;
|
||||
if (s.isToDisk()) {
|
||||
toDisk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (beginPhysical) {
|
||||
RepositoryChunk newChunk = null;
|
||||
if (toDisk) {
|
||||
newChunk = repository.newChunk(now);
|
||||
MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
|
||||
} else {
|
||||
MetadataRepository.getInstance().setOutput(null);
|
||||
}
|
||||
currentChunk = newChunk;
|
||||
jvm.beginRecording_();
|
||||
recording.setState(RecordingState.RUNNING);
|
||||
updateSettings();
|
||||
writeMetaEvents();
|
||||
} else {
|
||||
RepositoryChunk newChunk = null;
|
||||
if (toDisk) {
|
||||
newChunk = repository.newChunk(now);
|
||||
RequestEngine.doChunkEnd();
|
||||
MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
|
||||
}
|
||||
recording.setState(RecordingState.RUNNING);
|
||||
updateSettings();
|
||||
writeMetaEvents();
|
||||
if (currentChunk != null) {
|
||||
finishChunk(currentChunk, now, recording);
|
||||
}
|
||||
currentChunk = newChunk;
|
||||
}
|
||||
|
||||
RequestEngine.doChunkBegin();
|
||||
}
|
||||
|
||||
synchronized void stop(PlatformRecording recording) {
|
||||
RecordingState state = recording.getState();
|
||||
|
||||
if (Utils.isAfter(state, RecordingState.RUNNING)) {
|
||||
throw new IllegalStateException("Can't stop an already stopped recording.");
|
||||
}
|
||||
if (Utils.isBefore(state, RecordingState.RUNNING)) {
|
||||
throw new IllegalStateException("Recording must be started before it can be stopped.");
|
||||
}
|
||||
Instant now = Instant.now();
|
||||
boolean toDisk = false;
|
||||
boolean endPhysical = true;
|
||||
for (PlatformRecording s : getRecordings()) {
|
||||
RecordingState rs = s.getState();
|
||||
if (s != recording && RecordingState.RUNNING == rs) {
|
||||
endPhysical = false;
|
||||
if (s.isToDisk()) {
|
||||
toDisk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
OldObjectSample.emit(recording);
|
||||
|
||||
if (endPhysical) {
|
||||
RequestEngine.doChunkEnd();
|
||||
if (recording.isToDisk()) {
|
||||
if (currentChunk != null) {
|
||||
MetadataRepository.getInstance().setOutput(null);
|
||||
finishChunk(currentChunk, now, null);
|
||||
currentChunk = null;
|
||||
}
|
||||
} else {
|
||||
// last memory
|
||||
dumpMemoryToDestination(recording);
|
||||
}
|
||||
jvm.endRecording_();
|
||||
disableEvents();
|
||||
} else {
|
||||
RepositoryChunk newChunk = null;
|
||||
RequestEngine.doChunkEnd();
|
||||
updateSettingsButIgnoreRecording(recording);
|
||||
if (toDisk) {
|
||||
newChunk = repository.newChunk(now);
|
||||
MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
|
||||
} else {
|
||||
MetadataRepository.getInstance().setOutput(null);
|
||||
}
|
||||
writeMetaEvents();
|
||||
if (currentChunk != null) {
|
||||
finishChunk(currentChunk, now, null);
|
||||
}
|
||||
currentChunk = newChunk;
|
||||
RequestEngine.doChunkBegin();
|
||||
}
|
||||
recording.setState(RecordingState.STOPPED);
|
||||
}
|
||||
|
||||
private void dumpMemoryToDestination(PlatformRecording recording) {
|
||||
WriteableUserPath dest = recording.getDestination();
|
||||
if (dest != null) {
|
||||
MetadataRepository.getInstance().setOutput(dest.getRealPathText());
|
||||
recording.clearDestination();
|
||||
}
|
||||
}
|
||||
private void disableEvents() {
|
||||
MetadataRepository.getInstance().disableEvents();
|
||||
}
|
||||
|
||||
void updateSettings() {
|
||||
updateSettingsButIgnoreRecording(null);
|
||||
}
|
||||
|
||||
void updateSettingsButIgnoreRecording(PlatformRecording ignoreMe) {
|
||||
List<PlatformRecording> recordings = getRunningRecordings();
|
||||
List<Map<String, String>> list = new ArrayList<>(recordings.size());
|
||||
for (PlatformRecording r : recordings) {
|
||||
if (r != ignoreMe) {
|
||||
list.add(r.getSettings());
|
||||
}
|
||||
}
|
||||
MetadataRepository.getInstance().setSettings(list);
|
||||
}
|
||||
|
||||
synchronized void rotateDisk() {
|
||||
Instant now = Instant.now();
|
||||
RepositoryChunk newChunk = repository.newChunk(now);
|
||||
RequestEngine.doChunkEnd();
|
||||
MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
|
||||
writeMetaEvents();
|
||||
if (currentChunk != null) {
|
||||
finishChunk(currentChunk, now, null);
|
||||
}
|
||||
currentChunk = newChunk;
|
||||
RequestEngine.doChunkBegin();
|
||||
}
|
||||
|
||||
private List<PlatformRecording> getRunningRecordings() {
|
||||
List<PlatformRecording> runningRecordings = new ArrayList<>();
|
||||
for (PlatformRecording recording : getRecordings()) {
|
||||
if (recording.getState() == RecordingState.RUNNING) {
|
||||
runningRecordings.add(recording);
|
||||
}
|
||||
}
|
||||
return runningRecordings;
|
||||
}
|
||||
|
||||
private List<RepositoryChunk> makeChunkList(Instant startTime, Instant endTime) {
|
||||
Set<RepositoryChunk> chunkSet = new HashSet<>();
|
||||
for (PlatformRecording r : getRecordings()) {
|
||||
chunkSet.addAll(r.getChunks());
|
||||
}
|
||||
if (chunkSet.size() > 0) {
|
||||
List<RepositoryChunk> chunks = new ArrayList<>(chunkSet.size());
|
||||
for (RepositoryChunk rc : chunkSet) {
|
||||
if (rc.inInterval(startTime, endTime)) {
|
||||
chunks.add(rc);
|
||||
}
|
||||
}
|
||||
// n*log(n), should be able to do n*log(k) with a priority queue,
|
||||
// where k = number of recordings, n = number of chunks
|
||||
Collections.sort(chunks, RepositoryChunk.END_TIME_COMPARATOR);
|
||||
return chunks;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private void startDiskMonitor() {
|
||||
Thread t = SecuritySupport.createThreadWitNoPermissions("JFR Periodic Tasks", () -> periodicTask());
|
||||
SecuritySupport.setDaemonThread(t, true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void finishChunk(RepositoryChunk chunk, Instant time, PlatformRecording ignoreMe) {
|
||||
chunk.finish(time);
|
||||
for (PlatformRecording r : getRecordings()) {
|
||||
if (r != ignoreMe && r.getState() == RecordingState.RUNNING) {
|
||||
r.appendChunk(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMetaEvents() {
|
||||
|
||||
if (activeRecordingEvent.isEnabled()) {
|
||||
for (PlatformRecording r : getRecordings()) {
|
||||
if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) {
|
||||
ActiveRecordingEvent event = new ActiveRecordingEvent();
|
||||
event.id = r.getId();
|
||||
event.name = r.getName();
|
||||
WriteableUserPath p = r.getDestination();
|
||||
event.destination = p == null ? null : p.getRealPathText();
|
||||
Duration d = r.getDuration();
|
||||
event.recordingDuration = d == null ? Long.MAX_VALUE : d.toMillis();
|
||||
Duration age = r.getMaxAge();
|
||||
event.maxAge = age == null ? Long.MAX_VALUE : age.toMillis();
|
||||
Long size = r.getMaxSize();
|
||||
event.maxSize = size == null ? Long.MAX_VALUE : size;
|
||||
Instant start = r.getStartTime();
|
||||
event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli();
|
||||
event.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeSettingEvent.isEnabled()) {
|
||||
for (EventControl ec : MetadataRepository.getInstance().getEventControls()) {
|
||||
ec.writeActiveSettingEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void periodicTask() {
|
||||
if (!jvm.hasNativeJFR()) {
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
synchronized (this) {
|
||||
if (jvm.shouldRotateDisk()) {
|
||||
rotateDisk();
|
||||
}
|
||||
}
|
||||
long minDelta = RequestEngine.doPeriodic();
|
||||
long wait = Math.min(minDelta, Options.getWaitInterval());
|
||||
takeNap(wait);
|
||||
}
|
||||
}
|
||||
|
||||
private void takeNap(long duration) {
|
||||
try {
|
||||
synchronized (JVM.FILE_DELTA_CHANGE) {
|
||||
JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized Recording newCopy(PlatformRecording r, boolean stop) {
|
||||
Recording newRec = new Recording();
|
||||
PlatformRecording copy = PrivateAccess.getInstance().getPlatformRecording(newRec);
|
||||
copy.setSettings(r.getSettings());
|
||||
copy.setMaxAge(r.getMaxAge());
|
||||
copy.setMaxSize(r.getMaxSize());
|
||||
copy.setDumpOnExit(r.getDumpOnExit());
|
||||
copy.setName("Clone of " + r.getName());
|
||||
copy.setToDisk(r.isToDisk());
|
||||
copy.setInternalDuration(r.getDuration());
|
||||
copy.setStartTime(r.getStartTime());
|
||||
copy.setStopTime(r.getStopTime());
|
||||
|
||||
if (r.getState() == RecordingState.NEW) {
|
||||
return newRec;
|
||||
}
|
||||
if (r.getState() == RecordingState.DELAYED) {
|
||||
copy.scheduleStart(r.getStartTime());
|
||||
return newRec;
|
||||
}
|
||||
copy.setState(r.getState());
|
||||
// recording has started, copy chunks
|
||||
for (RepositoryChunk c : r.getChunks()) {
|
||||
copy.add(c);
|
||||
}
|
||||
if (r.getState() == RecordingState.RUNNING) {
|
||||
if (stop) {
|
||||
copy.stop("Stopped when cloning recording '" + r.getName() + "'");
|
||||
} else {
|
||||
if (r.getStopTime() != null) {
|
||||
TimerTask stopTask = copy.createStopTask();
|
||||
copy.setStopTask(copy.createStopTask());
|
||||
getTimer().schedule(stopTask, r.getStopTime().toEpochMilli());
|
||||
}
|
||||
}
|
||||
}
|
||||
return newRec;
|
||||
}
|
||||
|
||||
public synchronized void fillWithRecordedData(PlatformRecording target, Boolean pathToGcRoots) {
|
||||
boolean running = false;
|
||||
boolean toDisk = false;
|
||||
|
||||
for (PlatformRecording r : recordings) {
|
||||
if (r.getState() == RecordingState.RUNNING) {
|
||||
running = true;
|
||||
if (r.isToDisk()) {
|
||||
toDisk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If needed, flush data from memory
|
||||
if (running) {
|
||||
if (toDisk) {
|
||||
OldObjectSample.emit(recordings, pathToGcRoots);
|
||||
rotateDisk();
|
||||
} else {
|
||||
try (PlatformRecording snapshot = newTemporaryRecording()) {
|
||||
snapshot.setToDisk(true);
|
||||
snapshot.setShouldWriteActiveRecordingEvent(false);
|
||||
snapshot.start();
|
||||
OldObjectSample.emit(recordings, pathToGcRoots);
|
||||
snapshot.stop("Snapshot dump");
|
||||
fillWithDiskChunks(target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
fillWithDiskChunks(target);
|
||||
}
|
||||
|
||||
private void fillWithDiskChunks(PlatformRecording target) {
|
||||
for (RepositoryChunk c : makeChunkList(null, null)) {
|
||||
target.add(c);
|
||||
}
|
||||
target.setState(RecordingState.STOPPED);
|
||||
Instant startTime = null;
|
||||
Instant endTime = null;
|
||||
|
||||
for (RepositoryChunk c : target.getChunks()) {
|
||||
if (startTime == null || c.getStartTime().isBefore(startTime)) {
|
||||
startTime = c.getStartTime();
|
||||
}
|
||||
if (endTime == null || c.getEndTime().isAfter(endTime)) {
|
||||
endTime = c.getEndTime();
|
||||
}
|
||||
}
|
||||
Instant now = Instant.now();
|
||||
if (startTime == null) {
|
||||
startTime = now;
|
||||
}
|
||||
if (endTime == null) {
|
||||
endTime = now;
|
||||
}
|
||||
target.setStartTime(startTime);
|
||||
target.setStopTime(endTime);
|
||||
target.setInternalDuration(Duration.between(startTime, endTime));
|
||||
}
|
||||
}
|
||||
781
jdkSrc/jdk8/jdk/jfr/internal/PlatformRecording.java
Normal file
781
jdkSrc/jdk8/jdk/jfr/internal/PlatformRecording.java
Normal file
@@ -0,0 +1,781 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static jdk.jfr.internal.LogLevel.DEBUG;
|
||||
import static jdk.jfr.internal.LogLevel.WARN;
|
||||
import static jdk.jfr.internal.LogTag.JFR;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.TimerTask;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import jdk.jfr.Configuration;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
public final class PlatformRecording implements AutoCloseable {
|
||||
|
||||
private final PlatformRecorder recorder;
|
||||
private final long id;
|
||||
// Recording settings
|
||||
private Map<String, String> settings = new LinkedHashMap<>();
|
||||
private Duration duration;
|
||||
private Duration maxAge;
|
||||
private long maxSize;
|
||||
|
||||
private WriteableUserPath destination;
|
||||
|
||||
private boolean toDisk = true;
|
||||
private String name;
|
||||
private boolean dumpOnExit;
|
||||
private SafePath dumpOnExitDirectory = new SafePath(".");
|
||||
// Timestamp information
|
||||
private Instant stopTime;
|
||||
private Instant startTime;
|
||||
|
||||
// Misc, information
|
||||
private RecordingState state = RecordingState.NEW;
|
||||
private long size;
|
||||
private final LinkedList<RepositoryChunk> chunks = new LinkedList<>();
|
||||
private volatile Recording recording;
|
||||
private TimerTask stopTask;
|
||||
private TimerTask startTask;
|
||||
private AccessControlContext noDestinationDumpOnExitAccessControlContext;
|
||||
private boolean shuoldWriteActiveRecordingEvent = true;
|
||||
|
||||
PlatformRecording(PlatformRecorder recorder, long id) {
|
||||
// Typically the access control context is taken
|
||||
// when you call dump(Path) or setDdestination(Path),
|
||||
// but if no destination is set and dumponexit=true
|
||||
// the control context of the recording is taken when the
|
||||
// Recording object is constructed. This works well for
|
||||
// -XX:StartFlightRecording and JFR.dump
|
||||
this.noDestinationDumpOnExitAccessControlContext = AccessController.getContext();
|
||||
this.id = id;
|
||||
this.recorder = recorder;
|
||||
this.name = String.valueOf(id);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
RecordingState oldState;
|
||||
RecordingState newState;
|
||||
synchronized (recorder) {
|
||||
oldState = getState();
|
||||
if (!Utils.isBefore(state, RecordingState.RUNNING)) {
|
||||
throw new IllegalStateException("Recording can only be started once.");
|
||||
}
|
||||
if (startTask != null) {
|
||||
startTask.cancel();
|
||||
startTask = null;
|
||||
startTime = null;
|
||||
}
|
||||
recorder.start(this);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, () -> {
|
||||
// Only print non-default values so it easy to see
|
||||
// which options were added
|
||||
StringJoiner options = new StringJoiner(", ");
|
||||
if (!toDisk) {
|
||||
options.add("disk=false");
|
||||
}
|
||||
if (maxAge != null) {
|
||||
options.add("maxage=" + Utils.formatTimespan(maxAge, ""));
|
||||
}
|
||||
if (maxSize != 0) {
|
||||
options.add("maxsize=" + Utils.formatBytesCompact(maxSize));
|
||||
}
|
||||
if (dumpOnExit) {
|
||||
options.add("dumponexit=true");
|
||||
}
|
||||
if (duration != null) {
|
||||
options.add("duration=" + Utils.formatTimespan(duration, ""));
|
||||
}
|
||||
if (destination != null) {
|
||||
options.add("filename=" + destination.getRealPathText());
|
||||
}
|
||||
String optionText = options.toString();
|
||||
if (optionText.length() != 0) {
|
||||
optionText = "{" + optionText + "}";
|
||||
}
|
||||
return "Started recording \"" + getName() + "\" (" + getId() + ") " + optionText;
|
||||
});
|
||||
newState = getState();
|
||||
}
|
||||
notifyIfStateChanged(oldState, newState);
|
||||
}
|
||||
|
||||
public boolean stop(String reason) {
|
||||
RecordingState oldState;
|
||||
RecordingState newState;
|
||||
synchronized (recorder) {
|
||||
oldState = getState();
|
||||
if (stopTask != null) {
|
||||
stopTask.cancel();
|
||||
stopTask = null;
|
||||
}
|
||||
recorder.stop(this);
|
||||
String endText = reason == null ? "" : ". Reason \"" + reason + "\".";
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Stopped recording \"" + getName() + "\" (" + getId() + ")" + endText);
|
||||
this.stopTime = Instant.now();
|
||||
newState = getState();
|
||||
}
|
||||
WriteableUserPath dest = getDestination();
|
||||
|
||||
if (dest != null) {
|
||||
try {
|
||||
dumpStopped(dest);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Wrote recording \"" + getName() + "\" (" + getId() + ") to " + dest.getRealPathText());
|
||||
notifyIfStateChanged(newState, oldState);
|
||||
close(); // remove if copied out
|
||||
} catch(IOException e) {
|
||||
// throw e; // BUG8925030
|
||||
}
|
||||
} else {
|
||||
notifyIfStateChanged(newState, oldState);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void scheduleStart(Duration delay) {
|
||||
synchronized (recorder) {
|
||||
ensureOkForSchedule();
|
||||
|
||||
startTime = Instant.now().plus(delay);
|
||||
LocalDateTime now = LocalDateTime.now().plus(delay);
|
||||
setState(RecordingState.DELAYED);
|
||||
startTask = createStartTask();
|
||||
recorder.getTimer().schedule(startTask, delay.toMillis());
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Scheduled recording \"" + getName() + "\" (" + getId() + ") to start at " + now);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureOkForSchedule() {
|
||||
if (getState() != RecordingState.NEW) {
|
||||
throw new IllegalStateException("Only a new recoridng can be scheduled for start");
|
||||
}
|
||||
}
|
||||
|
||||
private TimerTask createStartTask() {
|
||||
// Taking ref. to recording here.
|
||||
// Opens up for memory leaks.
|
||||
return new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (recorder) {
|
||||
if (getState() != RecordingState.DELAYED) {
|
||||
return;
|
||||
}
|
||||
start();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void scheduleStart(Instant startTime) {
|
||||
synchronized (recorder) {
|
||||
ensureOkForSchedule();
|
||||
this.startTime = startTime;
|
||||
setState(RecordingState.DELAYED);
|
||||
startTask = createStartTask();
|
||||
recorder.getTimer().schedule(startTask, startTime.toEpochMilli());
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getSettings() {
|
||||
synchronized (recorder) {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Instant getStopTime() {
|
||||
synchronized (recorder) {
|
||||
return stopTime;
|
||||
}
|
||||
}
|
||||
|
||||
public Instant getStartTime() {
|
||||
synchronized (recorder) {
|
||||
return startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public Long getMaxSize() {
|
||||
synchronized (recorder) {
|
||||
return maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
public Duration getMaxAge() {
|
||||
synchronized (recorder) {
|
||||
return maxAge;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
synchronized (recorder) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public RecordingState getState() {
|
||||
synchronized (recorder) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
RecordingState oldState;
|
||||
RecordingState newState;
|
||||
|
||||
synchronized (recorder) {
|
||||
oldState = getState();
|
||||
if (RecordingState.CLOSED != getState()) {
|
||||
if (startTask != null) {
|
||||
startTask.cancel();
|
||||
startTask = null;
|
||||
}
|
||||
recorder.finish(this);
|
||||
for (RepositoryChunk c : chunks) {
|
||||
removed(c);
|
||||
}
|
||||
chunks.clear();
|
||||
setState(RecordingState.CLOSED);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Closed recording \"" + getName() + "\" (" + getId() + ")");
|
||||
}
|
||||
newState = getState();
|
||||
}
|
||||
notifyIfStateChanged(newState, oldState);
|
||||
}
|
||||
|
||||
// To be used internally when doing dumps.
|
||||
// Caller must have recorder lock and close recording before releasing lock
|
||||
public PlatformRecording newSnapshotClone(String reason, Boolean pathToGcRoots) throws IOException {
|
||||
if(!Thread.holdsLock(recorder)) {
|
||||
throw new InternalError("Caller must have recorder lock");
|
||||
}
|
||||
RecordingState state = getState();
|
||||
if (state == RecordingState.CLOSED) {
|
||||
throw new IOException("Recording \"" + name + "\" (id=" + id + ") has been closed, no contents to write");
|
||||
}
|
||||
if (state == RecordingState.DELAYED || state == RecordingState.NEW) {
|
||||
throw new IOException("Recording \"" + name + "\" (id=" + id + ") has not started, no contents to write");
|
||||
}
|
||||
if (state == RecordingState.STOPPED) {
|
||||
PlatformRecording clone = recorder.newTemporaryRecording();
|
||||
for (RepositoryChunk r : chunks) {
|
||||
clone.add(r);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
// Recording is RUNNING, create a clone
|
||||
PlatformRecording clone = recorder.newTemporaryRecording();
|
||||
clone.setShouldWriteActiveRecordingEvent(false);
|
||||
clone.setName(getName());
|
||||
clone.setToDisk(true);
|
||||
// We purposely don't clone settings here, since
|
||||
// a union a == a
|
||||
if (!isToDisk()) {
|
||||
// force memory contents to disk
|
||||
clone.start();
|
||||
} else {
|
||||
// using existing chunks on disk
|
||||
for (RepositoryChunk c : chunks) {
|
||||
clone.add(c);
|
||||
}
|
||||
clone.setState(RecordingState.RUNNING);
|
||||
clone.setStartTime(getStartTime());
|
||||
}
|
||||
if (pathToGcRoots == null) {
|
||||
clone.setSettings(getSettings()); // needed for old object sample
|
||||
clone.stop(reason); // dumps to destination path here
|
||||
} else {
|
||||
// Risk of violating lock order here, since
|
||||
// clone.stop() will take recorder lock inside
|
||||
// metadata lock, but OK if we already
|
||||
// have recorder lock when we entered metadata lock
|
||||
synchronized (MetadataRepository.getInstance()) {
|
||||
clone.setSettings(OldObjectSample.createSettingsForSnapshot(this, pathToGcRoots));
|
||||
clone.stop(reason);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
public boolean isToDisk() {
|
||||
synchronized (recorder) {
|
||||
return toDisk;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxSize(long maxSize) {
|
||||
synchronized (recorder) {
|
||||
if (getState() == RecordingState.CLOSED) {
|
||||
throw new IllegalStateException("Can't set max age when recording is closed");
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
trimToSize();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDestination(WriteableUserPath userSuppliedPath) throws IOException {
|
||||
synchronized (recorder) {
|
||||
if (Utils.isState(getState(), RecordingState.STOPPED, RecordingState.CLOSED)) {
|
||||
throw new IllegalStateException("Destination can't be set on a recording that has been stopped/closed");
|
||||
}
|
||||
this.destination = userSuppliedPath;
|
||||
}
|
||||
}
|
||||
|
||||
public WriteableUserPath getDestination() {
|
||||
synchronized (recorder) {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
void setState(RecordingState state) {
|
||||
synchronized (recorder) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
void setStartTime(Instant startTime) {
|
||||
synchronized (recorder) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
}
|
||||
|
||||
void setStopTime(Instant timeStamp) {
|
||||
synchronized (recorder) {
|
||||
stopTime = timeStamp;
|
||||
}
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
synchronized (recorder) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
synchronized (recorder) {
|
||||
ensureNotClosed();
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureNotClosed() {
|
||||
if (getState() == RecordingState.CLOSED) {
|
||||
throw new IllegalStateException("Can't change name on a closed recording");
|
||||
}
|
||||
}
|
||||
|
||||
public void setDumpOnExit(boolean dumpOnExit) {
|
||||
synchronized (recorder) {
|
||||
this.dumpOnExit = dumpOnExit;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getDumpOnExit() {
|
||||
synchronized (recorder) {
|
||||
return dumpOnExit;
|
||||
}
|
||||
}
|
||||
|
||||
public void setToDisk(boolean toDisk) {
|
||||
synchronized (recorder) {
|
||||
if (Utils.isState(getState(), RecordingState.NEW, RecordingState.DELAYED)) {
|
||||
this.toDisk = toDisk;
|
||||
} else {
|
||||
throw new IllegalStateException("Recording option disk can't be changed after recording has started");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSetting(String id, String value) {
|
||||
synchronized (recorder) {
|
||||
this.settings.put(id, value);
|
||||
if (getState() == RecordingState.RUNNING) {
|
||||
recorder.updateSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSettings(Map<String, String> settings) {
|
||||
setSettings(settings, true);
|
||||
}
|
||||
|
||||
private void setSettings(Map<String, String> settings, boolean update) {
|
||||
if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO) && update) {
|
||||
TreeMap<String, String> ordered = new TreeMap<>(settings);
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "New settings for recording \"" + getName() + "\" (" + getId() + ")");
|
||||
for (Map.Entry<String, String> entry : ordered.entrySet()) {
|
||||
String text = entry.getKey() + "=\"" + entry.getValue() + "\"";
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, text);
|
||||
}
|
||||
}
|
||||
synchronized (recorder) {
|
||||
this.settings = new LinkedHashMap<>(settings);
|
||||
if (getState() == RecordingState.RUNNING && update) {
|
||||
recorder.updateSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyIfStateChanged(RecordingState newState, RecordingState oldState) {
|
||||
if (oldState == newState) {
|
||||
return;
|
||||
}
|
||||
for (FlightRecorderListener cl : PlatformRecorder.getListeners()) {
|
||||
try {
|
||||
cl.recordingStateChanged(getRecording());
|
||||
} catch (RuntimeException re) {
|
||||
Logger.log(JFR, WARN, "Error notifying recorder listener:" + re.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setRecording(Recording recording) {
|
||||
this.recording = recording;
|
||||
}
|
||||
|
||||
public Recording getRecording() {
|
||||
return recording;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName() + " (id=" + getId() + ") " + getState();
|
||||
}
|
||||
|
||||
public void setConfiguration(Configuration c) {
|
||||
setSettings(c.getSettings());
|
||||
}
|
||||
|
||||
public void setMaxAge(Duration maxAge) {
|
||||
synchronized (recorder) {
|
||||
if (getState() == RecordingState.CLOSED) {
|
||||
throw new IllegalStateException("Can't set max age when recording is closed");
|
||||
}
|
||||
this.maxAge = maxAge;
|
||||
if (maxAge != null) {
|
||||
trimToAge(Instant.now().minus(maxAge));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void appendChunk(RepositoryChunk chunk) {
|
||||
if (!chunk.isFinished()) {
|
||||
throw new Error("not finished chunk " + chunk.getStartTime());
|
||||
}
|
||||
synchronized (recorder) {
|
||||
if (!toDisk) {
|
||||
return;
|
||||
}
|
||||
if (maxAge != null) {
|
||||
trimToAge(chunk.getEndTime().minus(maxAge));
|
||||
}
|
||||
chunks.addLast(chunk);
|
||||
added(chunk);
|
||||
trimToSize();
|
||||
}
|
||||
}
|
||||
|
||||
private void trimToSize() {
|
||||
if (maxSize == 0) {
|
||||
return;
|
||||
}
|
||||
while (size > maxSize && chunks.size() > 1) {
|
||||
RepositoryChunk c = chunks.removeFirst();
|
||||
removed(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void trimToAge(Instant oldest) {
|
||||
while (!chunks.isEmpty()) {
|
||||
RepositoryChunk oldestChunk = chunks.peek();
|
||||
if (oldestChunk.getEndTime().isAfter(oldest)) {
|
||||
return;
|
||||
}
|
||||
chunks.removeFirst();
|
||||
removed(oldestChunk);
|
||||
}
|
||||
}
|
||||
|
||||
void add(RepositoryChunk c) {
|
||||
chunks.add(c);
|
||||
added(c);
|
||||
}
|
||||
|
||||
private void added(RepositoryChunk c) {
|
||||
c.use();
|
||||
size += c.getSize();
|
||||
Logger.log(JFR, DEBUG, () -> "Recording \"" + name + "\" (" + id + ") added chunk " + c.toString() + ", current size=" + size);
|
||||
}
|
||||
|
||||
private void removed(RepositoryChunk c) {
|
||||
size -= c.getSize();
|
||||
Logger.log(JFR, DEBUG, () -> "Recording \"" + name + "\" (" + id + ") removed chunk " + c.toString() + ", current size=" + size);
|
||||
c.release();
|
||||
}
|
||||
|
||||
public List<RepositoryChunk> getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public InputStream open(Instant start, Instant end) throws IOException {
|
||||
synchronized (recorder) {
|
||||
if (getState() != RecordingState.STOPPED) {
|
||||
throw new IOException("Recording must be stopped before it can be read.");
|
||||
}
|
||||
List<RepositoryChunk> chunksToUse = new ArrayList<RepositoryChunk>();
|
||||
for (RepositoryChunk chunk : chunks) {
|
||||
if (chunk.isFinished()) {
|
||||
Instant chunkStart = chunk.getStartTime();
|
||||
Instant chunkEnd = chunk.getEndTime();
|
||||
if (start == null || !chunkEnd.isBefore(start)) {
|
||||
if (end == null || !chunkStart.isAfter(end)) {
|
||||
chunksToUse.add(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chunksToUse.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new ChunkInputStream(chunksToUse);
|
||||
}
|
||||
}
|
||||
|
||||
public Duration getDuration() {
|
||||
synchronized (recorder) {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
void setInternalDuration(Duration duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public void setDuration(Duration duration) {
|
||||
synchronized (recorder) {
|
||||
if (Utils.isState(getState(), RecordingState.STOPPED, RecordingState.CLOSED)) {
|
||||
throw new IllegalStateException("Duration can't be set after a recording has been stopped/closed");
|
||||
}
|
||||
setInternalDuration(duration);
|
||||
if (getState() != RecordingState.NEW) {
|
||||
updateTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateTimer() {
|
||||
if (stopTask != null) {
|
||||
stopTask.cancel();
|
||||
stopTask = null;
|
||||
}
|
||||
if (getState() == RecordingState.CLOSED) {
|
||||
return;
|
||||
}
|
||||
if (duration != null) {
|
||||
stopTask = createStopTask();
|
||||
recorder.getTimer().schedule(stopTask, new Date(startTime.plus(duration).toEpochMilli()));
|
||||
}
|
||||
}
|
||||
|
||||
TimerTask createStopTask() {
|
||||
return new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
stop("End of duration reached");
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not stop recording.");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Recording newCopy(boolean stop) {
|
||||
return recorder.newCopy(this, stop);
|
||||
}
|
||||
|
||||
void setStopTask(TimerTask stopTask) {
|
||||
synchronized (recorder) {
|
||||
this.stopTask = stopTask;
|
||||
}
|
||||
}
|
||||
|
||||
void clearDestination() {
|
||||
destination = null;
|
||||
}
|
||||
|
||||
public AccessControlContext getNoDestinationDumpOnExitAccessControlContext() {
|
||||
return noDestinationDumpOnExitAccessControlContext;
|
||||
}
|
||||
|
||||
void setShouldWriteActiveRecordingEvent(boolean shouldWrite) {
|
||||
this.shuoldWriteActiveRecordingEvent = shouldWrite;
|
||||
}
|
||||
|
||||
boolean shouldWriteMetadataEvent() {
|
||||
return shuoldWriteActiveRecordingEvent;
|
||||
}
|
||||
|
||||
// Dump running and stopped recordings
|
||||
public void dump(WriteableUserPath writeableUserPath) throws IOException {
|
||||
synchronized (recorder) {
|
||||
try(PlatformRecording p = newSnapshotClone("Dumped by user", null)) {
|
||||
p.dumpStopped(writeableUserPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpStopped(WriteableUserPath userPath) throws IOException {
|
||||
synchronized (recorder) {
|
||||
userPath.doPriviligedIO(() -> {
|
||||
try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(userPath.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
|
||||
long bytes = cc.transferTo(fc);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Transferred " + bytes + " bytes from the disk repository");
|
||||
// No need to force if no data was transferred, which avoids IOException when device is /dev/null
|
||||
if (bytes != 0) {
|
||||
fc.force(true);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void filter(Instant begin, Instant end, Long maxSize) {
|
||||
synchronized (recorder) {
|
||||
List<RepositoryChunk> result = removeAfter(end, removeBefore(begin, new ArrayList<>(chunks)));
|
||||
if (maxSize != null) {
|
||||
if (begin != null && end == null) {
|
||||
result = reduceFromBeginning(maxSize, result);
|
||||
} else {
|
||||
result = reduceFromEnd(maxSize, result);
|
||||
}
|
||||
}
|
||||
int size = 0;
|
||||
for (RepositoryChunk r : result) {
|
||||
size += r.getSize();
|
||||
r.use();
|
||||
}
|
||||
this.size = size;
|
||||
for (RepositoryChunk r : chunks) {
|
||||
r.release();
|
||||
}
|
||||
chunks.clear();
|
||||
chunks.addAll(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<RepositoryChunk> removeBefore(Instant time, List<RepositoryChunk> input) {
|
||||
if (time == null) {
|
||||
return input;
|
||||
}
|
||||
List<RepositoryChunk> result = new ArrayList<>(input.size());
|
||||
for (RepositoryChunk r : input) {
|
||||
if (!r.getEndTime().isBefore(time)) {
|
||||
result.add(r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<RepositoryChunk> removeAfter(Instant time, List<RepositoryChunk> input) {
|
||||
if (time == null) {
|
||||
return input;
|
||||
}
|
||||
List<RepositoryChunk> result = new ArrayList<>(input.size());
|
||||
for (RepositoryChunk r : input) {
|
||||
if (!r.getStartTime().isAfter(time)) {
|
||||
result.add(r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<RepositoryChunk> reduceFromBeginning(Long maxSize, List<RepositoryChunk> input) {
|
||||
if (maxSize == null || input.isEmpty()) {
|
||||
return input;
|
||||
}
|
||||
List<RepositoryChunk> result = new ArrayList<>(input.size());
|
||||
long total = 0;
|
||||
for (RepositoryChunk r : input) {
|
||||
total += r.getSize();
|
||||
if (total > maxSize) {
|
||||
break;
|
||||
}
|
||||
result.add(r);
|
||||
}
|
||||
// always keep at least one chunk
|
||||
if (result.isEmpty()) {
|
||||
result.add(input.get(0));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<RepositoryChunk> reduceFromEnd(Long maxSize, List<RepositoryChunk> input) {
|
||||
Collections.reverse(input);
|
||||
List<RepositoryChunk> result = reduceFromBeginning(maxSize, input);
|
||||
Collections.reverse(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setDumpOnExitDirectory(SafePath directory) {
|
||||
this.dumpOnExitDirectory = directory;
|
||||
}
|
||||
|
||||
public SafePath getDumpOnExitDirectory() {
|
||||
return this.dumpOnExitDirectory;
|
||||
}
|
||||
}
|
||||
97
jdkSrc/jdk8/jdk/jfr/internal/PrivateAccess.java
Normal file
97
jdkSrc/jdk8/jdk/jfr/internal/PrivateAccess.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Configuration;
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.FlightRecorderPermission;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
|
||||
/**
|
||||
* Provides access to package private function in jdk.jfr.
|
||||
* <p>
|
||||
* The static initializer in this class loads the Settings class, which will
|
||||
* call {@link #setPrivateAccess(PrivateAccess)} on this class, which can be
|
||||
* used call to package protected methods.
|
||||
*
|
||||
* This is similar to how java.lang accesses package private methods in
|
||||
* java.lang.reflect.
|
||||
*/
|
||||
public abstract class PrivateAccess {
|
||||
private volatile static PrivateAccess instance;
|
||||
|
||||
public static PrivateAccess getInstance() {
|
||||
// Can't be initialized in <clinit> because it may
|
||||
// deadlock with FlightRecordeerPermission.<clinit>
|
||||
if (instance == null) {
|
||||
// Will trigger
|
||||
// FlightRecordeerPermission.<clinit>
|
||||
// which will call PrivateAccess.setPrivateAccess
|
||||
new FlightRecorderPermission(Utils.REGISTER_EVENT);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void setPrivateAccess(PrivateAccess pa) {
|
||||
instance = pa;
|
||||
}
|
||||
|
||||
public abstract Type getType(Object o);
|
||||
|
||||
public abstract Configuration newConfiguration(String name, String label, String description, String provider, Map<String,String> settings, String contents);
|
||||
|
||||
public abstract EventType newEventType(PlatformEventType eventTypes);
|
||||
|
||||
public abstract AnnotationElement newAnnotation(Type annotationType, List<Object> values, boolean boot);
|
||||
|
||||
public abstract ValueDescriptor newValueDescriptor(String name, Type fieldType, List<AnnotationElement> annotations, int dimension, boolean constantPool, String fieldName);
|
||||
|
||||
public abstract PlatformRecording getPlatformRecording(Recording r);
|
||||
|
||||
public abstract PlatformEventType getPlatformEventType(EventType eventType);
|
||||
|
||||
public abstract boolean isConstantPool(ValueDescriptor v);
|
||||
|
||||
public abstract String getFieldName(ValueDescriptor v);
|
||||
|
||||
public abstract ValueDescriptor newValueDescriptor(Class<?> type, String name);
|
||||
|
||||
public abstract SettingDescriptor newSettingDescriptor(Type type, String name, String def, List<AnnotationElement> aes);
|
||||
|
||||
public abstract void setAnnotations(ValueDescriptor v, List<AnnotationElement> a);
|
||||
|
||||
public abstract void setAnnotations(SettingDescriptor s, List<AnnotationElement> a);
|
||||
|
||||
public abstract boolean isUnsigned(ValueDescriptor v);
|
||||
|
||||
public abstract PlatformRecorder getPlatformRecorder();
|
||||
}
|
||||
165
jdkSrc/jdk8/jdk/jfr/internal/Repository.java
Normal file
165
jdkSrc/jdk8/jdk/jfr/internal/Repository.java
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
public final class Repository {
|
||||
|
||||
private static final int MAX_REPO_CREATION_RETRIES = 1000;
|
||||
private static final JVM jvm = JVM.getJVM();
|
||||
private static final Repository instance = new Repository();
|
||||
|
||||
public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
|
||||
.ofPattern("yyyy_MM_dd_HH_mm_ss");
|
||||
|
||||
private final Set<SafePath> cleanupDirectories = new HashSet<>();
|
||||
private SafePath baseLocation;
|
||||
private SafePath repository;
|
||||
|
||||
private Repository() {
|
||||
}
|
||||
|
||||
public static Repository getRepository() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public synchronized void setBasePath(SafePath baseLocation) throws Exception {
|
||||
// Probe to see if repository can be created, needed for fail fast
|
||||
// during JVM startup or JFR.configure
|
||||
this.repository = createRepository(baseLocation);
|
||||
try {
|
||||
// Remove so we don't "leak" repositories, if JFR is never started
|
||||
// and shutdown hook not added.
|
||||
SecuritySupport.delete(repository);
|
||||
} catch (IOException ioe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository);
|
||||
}
|
||||
this.baseLocation = baseLocation;
|
||||
}
|
||||
|
||||
synchronized void ensureRepository() throws Exception {
|
||||
if (baseLocation == null) {
|
||||
setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized RepositoryChunk newChunk(Instant timestamp) {
|
||||
try {
|
||||
if (!SecuritySupport.existDirectory(repository)) {
|
||||
this.repository = createRepository(baseLocation);
|
||||
jvm.setRepositoryLocation(repository.toString());
|
||||
cleanupDirectories.add(repository);
|
||||
}
|
||||
return new RepositoryChunk(repository, timestamp);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format("Could not create chunk in repository %s, %s", repository, e.getMessage());
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, errorMsg);
|
||||
jvm.abort(errorMsg);
|
||||
throw new InternalError("Could not abort after JFR disk creation error");
|
||||
}
|
||||
}
|
||||
|
||||
private static SafePath createRepository(SafePath basePath) throws Exception {
|
||||
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
|
||||
SafePath f = null;
|
||||
|
||||
String basename = REPO_DATE_FORMAT.format(LocalDateTime.now()) + "_" + JVM.getJVM().getPid();
|
||||
String name = basename;
|
||||
|
||||
int i = 0;
|
||||
for (; i < MAX_REPO_CREATION_RETRIES; i++) {
|
||||
f = new SafePath(canonicalBaseRepositoryPath.toPath().resolve(name));
|
||||
if (tryToUseAsRepository(f)) {
|
||||
break;
|
||||
}
|
||||
name = basename + "_" + i;
|
||||
}
|
||||
|
||||
if (i == MAX_REPO_CREATION_RETRIES) {
|
||||
throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")");
|
||||
}
|
||||
SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
|
||||
return canonicalRepositoryPath;
|
||||
}
|
||||
|
||||
private static SafePath createRealBasePath(SafePath safePath) throws Exception {
|
||||
if (SecuritySupport.exists(safePath)) {
|
||||
if (!SecuritySupport.isWritable(safePath)) {
|
||||
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
|
||||
}
|
||||
return SecuritySupport.toRealPath(safePath);
|
||||
}
|
||||
SafePath p = SecuritySupport.createDirectories(safePath);
|
||||
return SecuritySupport.toRealPath(p);
|
||||
}
|
||||
|
||||
private static boolean tryToUseAsRepository(final SafePath path) {
|
||||
Path parent = path.toPath().getParent();
|
||||
if (parent == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
SecuritySupport.createDirectories(path);
|
||||
} catch (Exception e) {
|
||||
// file already existed or some other problem occurred
|
||||
}
|
||||
if (!SecuritySupport.exists(path)) {
|
||||
return false;
|
||||
}
|
||||
if (!SecuritySupport.isDirectory(path)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (IOException io) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void clear() {
|
||||
for (SafePath p : cleanupDirectories) {
|
||||
try {
|
||||
SecuritySupport.clearDirectory(p);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized SafePath getRepositoryPath() {
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
210
jdkSrc/jdk8/jdk/jfr/internal/RepositoryChunk.java
Normal file
210
jdkSrc/jdk8/jdk/jfr/internal/RepositoryChunk.java
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
final class RepositoryChunk {
|
||||
private static final int MAX_CHUNK_NAMES = 100;
|
||||
|
||||
static final Comparator<RepositoryChunk> END_TIME_COMPARATOR = new Comparator<RepositoryChunk>() {
|
||||
@Override
|
||||
public int compare(RepositoryChunk c1, RepositoryChunk c2) {
|
||||
return c1.endTime.compareTo(c2.endTime);
|
||||
}
|
||||
};
|
||||
|
||||
private final SafePath repositoryPath;
|
||||
private final SafePath unFinishedFile;
|
||||
private final SafePath file;
|
||||
private final Instant startTime;
|
||||
private final RandomAccessFile unFinishedRAF;
|
||||
|
||||
private Instant endTime = null; // unfinished
|
||||
private int refCount = 0;
|
||||
private long size;
|
||||
|
||||
RepositoryChunk(SafePath path, Instant startTime) throws Exception {
|
||||
ZonedDateTime z = ZonedDateTime.now();
|
||||
String fileName = Repository.REPO_DATE_FORMAT.format(
|
||||
LocalDateTime.ofInstant(startTime, z.getZone()));
|
||||
this.startTime = startTime;
|
||||
this.repositoryPath = path;
|
||||
this.unFinishedFile = findFileName(repositoryPath, fileName, ".part");
|
||||
this.file = findFileName(repositoryPath, fileName, ".jfr");
|
||||
this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile);
|
||||
SecuritySupport.touch(file);
|
||||
}
|
||||
|
||||
private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception {
|
||||
Path p = directory.toPath().resolve(name + extension);
|
||||
for (int i = 1; i < MAX_CHUNK_NAMES; i++) {
|
||||
SafePath s = new SafePath(p);
|
||||
if (!SecuritySupport.exists(s)) {
|
||||
return s;
|
||||
}
|
||||
String extendedName = String.format("%s_%02d%s", name, i, extension);
|
||||
p = directory.toPath().resolve(extendedName);
|
||||
}
|
||||
p = directory.toPath().resolve(name + "_" + System.currentTimeMillis() + extension);
|
||||
return SecuritySupport.toRealPath(new SafePath(p));
|
||||
}
|
||||
|
||||
public SafePath getUnfishedFile() {
|
||||
return unFinishedFile;
|
||||
}
|
||||
|
||||
void finish(Instant endTime) {
|
||||
try {
|
||||
finishWithException(endTime);
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not finish chunk. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void finishWithException(Instant endTime) throws IOException {
|
||||
unFinishedRAF.close();
|
||||
this.size = finish(unFinishedFile, file);
|
||||
this.endTime = endTime;
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Chunk finished: " + file);
|
||||
}
|
||||
|
||||
private static long finish(SafePath unFinishedFile, SafePath file) throws IOException {
|
||||
Objects.requireNonNull(unFinishedFile);
|
||||
Objects.requireNonNull(file);
|
||||
SecuritySupport.delete(file);
|
||||
SecuritySupport.moveReplace(unFinishedFile, file);
|
||||
return SecuritySupport.getFileSize(file);
|
||||
}
|
||||
|
||||
public Instant getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public Instant getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
private void delete(SafePath f) {
|
||||
try {
|
||||
SecuritySupport.delete(f);
|
||||
Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted");
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
|
||||
if (f != null) {
|
||||
SecuritySupport.deleteOnExit(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void destroy() {
|
||||
if (!isFinished()) {
|
||||
finish(Instant.MIN);
|
||||
}
|
||||
if (file != null) {
|
||||
delete(file);
|
||||
}
|
||||
try {
|
||||
unFinishedRAF.close();
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Could not close random access file: " + unFinishedFile.toString() + ". File will not be deleted due to: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void use() {
|
||||
++refCount;
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Use chunk " + toString() + " ref count now " + refCount);
|
||||
}
|
||||
|
||||
public synchronized void release() {
|
||||
--refCount;
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Release chunk " + toString() + " ref count now " + refCount);
|
||||
if (refCount == 0) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
boolean destroy = false;
|
||||
synchronized (this) {
|
||||
if (refCount > 0) {
|
||||
destroy = true;
|
||||
}
|
||||
}
|
||||
if (destroy) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return endTime != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isFinished()) {
|
||||
return file.toString();
|
||||
}
|
||||
return unFinishedFile.toString();
|
||||
}
|
||||
|
||||
ReadableByteChannel newChannel() throws IOException {
|
||||
if (!isFinished()) {
|
||||
throw new IOException("Chunk not finished");
|
||||
}
|
||||
return ((SecuritySupport.newFileChannelToRead(file)));
|
||||
}
|
||||
|
||||
public boolean inInterval(Instant startTime, Instant endTime) {
|
||||
if (startTime != null && getEndTime().isBefore(startTime)) {
|
||||
return false;
|
||||
}
|
||||
if (endTime != null && getStartTime().isAfter(endTime)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public SafePath getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
260
jdkSrc/jdk8/jdk/jfr/internal/RequestEngine.java
Normal file
260
jdkSrc/jdk8/jdk/jfr/internal/RequestEngine.java
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Predicate;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.EventType;
|
||||
|
||||
public final class RequestEngine {
|
||||
|
||||
private final static JVM jvm = JVM.getJVM();
|
||||
|
||||
final static class RequestHook {
|
||||
private final Runnable hook;
|
||||
private final PlatformEventType type;
|
||||
private final AccessControlContext accessControllerContext;
|
||||
private long delta;
|
||||
|
||||
// Java events
|
||||
private RequestHook(AccessControlContext acc, PlatformEventType eventType, Runnable hook) {
|
||||
this.hook = hook;
|
||||
this.type = eventType;
|
||||
this.accessControllerContext = acc;
|
||||
}
|
||||
|
||||
// native events
|
||||
RequestHook(PlatformEventType eventType) {
|
||||
this(null, eventType, null);
|
||||
}
|
||||
|
||||
private void execute() {
|
||||
try {
|
||||
if (accessControllerContext == null) { // native
|
||||
if (type.isJDK()) {
|
||||
hook.run();
|
||||
} else {
|
||||
jvm.emitEvent(type.getId(), JVM.counterTime(), 0);
|
||||
}
|
||||
if (Logger.shouldLog(LogTag.JFR_EVENT, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.DEBUG, ()-> "Executed periodic hook for " + type.getLogName());
|
||||
}
|
||||
} else {
|
||||
executeSecure();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.WARN, "Exception occured during execution of period hook for " + type.getLogName());
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSecure() {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
try {
|
||||
hook.run();
|
||||
if (Logger.shouldLog(LogTag.JFR_EVENT, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_EVENT, LogLevel.DEBUG, ()-> "Executed periodic hook for " + type.getLogName());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_EVENT, LogLevel.WARN, "Exception occured during execution of period hook for " + type.getLogName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, accessControllerContext);
|
||||
}
|
||||
}
|
||||
|
||||
private final static List<RequestHook> entries = new CopyOnWriteArrayList<>();
|
||||
private static long lastTimeMillis;
|
||||
|
||||
public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) {
|
||||
Objects.requireNonNull(acc);
|
||||
addHookInternal(acc, type, hook);
|
||||
}
|
||||
|
||||
private static void addHookInternal(AccessControlContext acc, PlatformEventType type, Runnable hook) {
|
||||
RequestHook he = new RequestHook(acc, type, hook);
|
||||
for (RequestHook e : entries) {
|
||||
if (e.hook == hook) {
|
||||
throw new IllegalArgumentException("Hook has already been added");
|
||||
}
|
||||
}
|
||||
he.type.setEventHook(true);
|
||||
// Insertion takes O(2*n), could be O(1) with HashMap, but
|
||||
// thinking is that CopyOnWriteArrayList is faster
|
||||
// to iterate over, which will happen more over time.
|
||||
entries.add(he);
|
||||
logHook("Added", type);
|
||||
}
|
||||
|
||||
public static void addTrustedJDKHook(Class<? extends Event> eventClass, Runnable runnable) {
|
||||
if (eventClass.getClassLoader() != null) {
|
||||
throw new SecurityException("Hook can only be registered for event classes that are loaded by the bootstrap class loader");
|
||||
}
|
||||
if (runnable.getClass().getClassLoader() != null) {
|
||||
throw new SecurityException("Runnable hook class must be loaded by the bootstrap class loader");
|
||||
}
|
||||
EventType eType = MetadataRepository.getInstance().getEventType(eventClass);
|
||||
PlatformEventType pType = PrivateAccess.getInstance().getPlatformEventType(eType);
|
||||
addHookInternal(null, pType, runnable);
|
||||
}
|
||||
|
||||
private static void logHook(String action, PlatformEventType type) {
|
||||
if (type.isJDK() || type.isJVM()) {
|
||||
Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.INFO, action + " periodic hook for " + type.getLogName());
|
||||
} else {
|
||||
Logger.log(LogTag.JFR_EVENT, LogLevel.INFO, action + " periodic hook for " + type.getLogName());
|
||||
}
|
||||
}
|
||||
|
||||
// Takes O(2*n), see addHook.
|
||||
public static boolean removeHook(Runnable hook) {
|
||||
for (RequestHook rh : entries) {
|
||||
if (rh.hook == hook) {
|
||||
entries.remove(rh);
|
||||
rh.type.setEventHook(false);
|
||||
logHook("Removed", rh.type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only to be used for JVM events. No access control contest
|
||||
// or check if hook already exists
|
||||
static void addHooks(List<RequestHook> newEntries) {
|
||||
List<RequestHook> addEntries = new ArrayList<>();
|
||||
for (RequestHook rh : newEntries) {
|
||||
rh.type.setEventHook(true);
|
||||
addEntries.add(rh);
|
||||
logHook("Added", rh.type);
|
||||
}
|
||||
entries.addAll(newEntries);
|
||||
}
|
||||
|
||||
static void doChunkEnd() {
|
||||
doChunk(x -> x.isEndChunk());
|
||||
}
|
||||
|
||||
static void doChunkBegin() {
|
||||
doChunk(x -> x.isBeginChunk());
|
||||
}
|
||||
|
||||
private static void doChunk(Predicate<PlatformEventType> predicate) {
|
||||
for (RequestHook requestHook : entries) {
|
||||
PlatformEventType s = requestHook.type;
|
||||
if (s.isEnabled() && predicate.test(s)) {
|
||||
requestHook.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static long doPeriodic() {
|
||||
return run_requests(entries);
|
||||
}
|
||||
|
||||
// code copied from native impl.
|
||||
private static long run_requests(Collection<RequestHook> entries) {
|
||||
long last = lastTimeMillis;
|
||||
// Bug 9000556 - current time millis has rather lame resolution
|
||||
// The use of os::elapsed_counter() is deliberate here, we don't
|
||||
// want it exchanged for os::ft_elapsed_counter().
|
||||
// Keeping direct call os::elapsed_counter() here for reliable
|
||||
// real time values in order to decide when registered requestable
|
||||
// events are due.
|
||||
long now = System.currentTimeMillis();
|
||||
long min = 0;
|
||||
long delta = 0;
|
||||
|
||||
if (last == 0) {
|
||||
last = now;
|
||||
}
|
||||
|
||||
// time from then to now
|
||||
delta = now - last;
|
||||
|
||||
if (delta < 0) {
|
||||
// to handle time adjustments
|
||||
// for example Daylight Savings
|
||||
lastTimeMillis = now;
|
||||
return 0;
|
||||
}
|
||||
for (RequestHook he : entries) {
|
||||
long left = 0;
|
||||
PlatformEventType es = he.type;
|
||||
// Not enabled, skip.
|
||||
if (!es.isEnabled() || es.isEveryChunk()) {
|
||||
continue;
|
||||
}
|
||||
long r_period = es.getPeriod();
|
||||
long r_delta = he.delta;
|
||||
|
||||
// add time elapsed.
|
||||
r_delta += delta;
|
||||
|
||||
// above threshold?
|
||||
if (r_delta >= r_period) {
|
||||
// Bug 9000556 - don't try to compensate
|
||||
// for wait > period
|
||||
r_delta = 0;
|
||||
he.execute();
|
||||
;
|
||||
}
|
||||
|
||||
// calculate time left
|
||||
left = (r_period - r_delta);
|
||||
|
||||
/**
|
||||
* nothing outside checks that a period is >= 0, so left can end up
|
||||
* negative here. ex. (r_period =(-1)) - (r_delta = 0) if it is,
|
||||
* handle it.
|
||||
*/
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
// assign delta back
|
||||
he.delta = r_delta;
|
||||
|
||||
if (min == 0 || left < min) {
|
||||
min = left;
|
||||
}
|
||||
}
|
||||
lastTimeMillis = now;
|
||||
return min;
|
||||
}
|
||||
}
|
||||
391
jdkSrc/jdk8/jdk/jfr/internal/SecuritySupport.java
Normal file
391
jdkSrc/jdk8/jdk/jfr/internal/SecuritySupport.java
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ReflectPermission;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Permission;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
import jdk.jfr.FlightRecorderPermission;
|
||||
import jdk.jfr.Recording;
|
||||
|
||||
/**
|
||||
* Contains JFR code that does
|
||||
* {@link AccessController#doPrivileged(PrivilegedAction)}
|
||||
*/
|
||||
public final class SecuritySupport {
|
||||
private final static Unsafe unsafe = Unsafe.getUnsafe();
|
||||
public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
|
||||
|
||||
static final SafePath USER_HOME = getPathInProperty("user.home", null);
|
||||
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
|
||||
|
||||
final static class SecureRecorderListener implements FlightRecorderListener {
|
||||
|
||||
private final AccessControlContext context;
|
||||
private final FlightRecorderListener changeListener;
|
||||
|
||||
SecureRecorderListener(AccessControlContext context, FlightRecorderListener changeListener) {
|
||||
this.context = Objects.requireNonNull(context);
|
||||
this.changeListener = Objects.requireNonNull(changeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordingStateChanged(Recording recording) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
try {
|
||||
changeListener.recordingStateChanged(recording);
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " at recording state change");
|
||||
}
|
||||
return null;
|
||||
}, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recorderInitialized(FlightRecorder recorder) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
try {
|
||||
changeListener.recorderInitialized(recorder);
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " when initializing FlightRecorder");
|
||||
}
|
||||
return null;
|
||||
}, context);
|
||||
}
|
||||
|
||||
public FlightRecorderListener getChangeListener() {
|
||||
return changeListener;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DirectoryCleaner extends SimpleFileVisitor<Path> {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(path);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
if (exc != null) {
|
||||
throw exc;
|
||||
}
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Path created by the default file provider,and not
|
||||
* a malicious provider.
|
||||
*
|
||||
*/
|
||||
public static final class SafePath {
|
||||
private final Path path;
|
||||
private final String text;
|
||||
|
||||
public SafePath(Path p) {
|
||||
// sanitize
|
||||
text = p.toString();
|
||||
path = Paths.get(text);
|
||||
}
|
||||
|
||||
public SafePath(String path) {
|
||||
this(Paths.get(path));
|
||||
}
|
||||
|
||||
public Path toPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
private interface RunnableWithCheckedException {
|
||||
public void run() throws Exception;
|
||||
}
|
||||
|
||||
private interface CallableWithoutCheckException<T> {
|
||||
public T call();
|
||||
}
|
||||
|
||||
private static <U> U doPrivilegedIOWithReturn(Callable<U> function) throws IOException {
|
||||
try {
|
||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<U>() {
|
||||
@Override
|
||||
public U run() throws Exception {
|
||||
return function.call();
|
||||
}
|
||||
}, null);
|
||||
} catch (PrivilegedActionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) {
|
||||
throw (IOException) t;
|
||||
}
|
||||
throw new IOException("Unexpected error during I/O operation. " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void doPriviligedIO(RunnableWithCheckedException function) throws IOException {
|
||||
doPrivilegedIOWithReturn(() -> {
|
||||
function.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private static void doPrivileged(Runnable function, Permission... perms) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
function.run();
|
||||
return null;
|
||||
}
|
||||
}, null, perms);
|
||||
}
|
||||
|
||||
private static void doPrivileged(Runnable function) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
function.run();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static <T> T doPrivilegedWithReturn(CallableWithoutCheckException<T> function, Permission... perms) {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<T>() {
|
||||
@Override
|
||||
public T run() {
|
||||
return function.call();
|
||||
}
|
||||
}, null, perms);
|
||||
}
|
||||
|
||||
public static List<SafePath> getPredefinedJFCFiles() {
|
||||
List<SafePath> list = new ArrayList<>();
|
||||
try {
|
||||
Iterator<Path> pathIterator = doPrivilegedIOWithReturn(() -> {
|
||||
return Files.newDirectoryStream(JFC_DIRECTORY.toPath(), "*").iterator();
|
||||
});
|
||||
while (pathIterator.hasNext()) {
|
||||
Path path = pathIterator.next();
|
||||
if (path.toString().endsWith(".jfc")) {
|
||||
list.add(new SafePath(path));
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static void makeVisibleToJFR(Class<?> clazz) {
|
||||
// nothing to do for JDK8
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a qualified export of the internal.jdk.jfr.internal.handlers package
|
||||
* (for EventHandler)
|
||||
*/
|
||||
static void addHandlerExport(Class<?> clazz) {
|
||||
// nothing to do for JDK8
|
||||
}
|
||||
|
||||
public static void registerEvent(Class<? extends Event> eventClass) {
|
||||
doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
|
||||
}
|
||||
|
||||
static boolean getBooleanProperty(String propertyName) {
|
||||
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
|
||||
}
|
||||
|
||||
private static SafePath getPathInProperty(String prop, String subPath) {
|
||||
return doPrivilegedWithReturn(() -> {
|
||||
String path = System.getProperty(prop);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
File file = subPath == null ? new File(path) : new File(path, subPath);
|
||||
return new SafePath(file.getAbsolutePath());
|
||||
}, new PropertyPermission("*", "read"));
|
||||
}
|
||||
|
||||
// Called by JVM during initialization of JFR
|
||||
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
|
||||
// The thread should have permission = new Permission[0], and not "modifyThreadGroup" and "modifyThread" on the stack,
|
||||
// but it's hard circumvent if we are going to pass in system thread group in the constructor
|
||||
Thread thread = doPrivilegedWithReturn(() -> new Thread(systemThreadGroup, "JFR Recorder Thread"), new RuntimePermission("modifyThreadGroup"), new RuntimePermission("modifyThread"));
|
||||
doPrivileged(() -> thread.setContextClassLoader(contextClassLoader), new RuntimePermission("setContextClassLoader"), new RuntimePermission("modifyThread"));
|
||||
return thread;
|
||||
}
|
||||
|
||||
static void registerShutdownHook(Thread shutdownHook) {
|
||||
doPrivileged(() -> Runtime.getRuntime().addShutdownHook(shutdownHook), new RuntimePermission("shutdownHooks"));
|
||||
}
|
||||
|
||||
static void setUncaughtExceptionHandler(Thread thread, Thread.UncaughtExceptionHandler eh) {
|
||||
doPrivileged(() -> thread.setUncaughtExceptionHandler(eh), new RuntimePermission("modifyThread"));
|
||||
}
|
||||
|
||||
static void moveReplace(SafePath from, SafePath to) throws IOException {
|
||||
doPrivilegedIOWithReturn(() -> Files.move(from.toPath(), to.toPath()));
|
||||
}
|
||||
|
||||
static void clearDirectory(SafePath safePath) throws IOException {
|
||||
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
|
||||
}
|
||||
|
||||
static SafePath toRealPath(SafePath safePath) throws Exception {
|
||||
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath()));
|
||||
}
|
||||
|
||||
static boolean existDirectory(SafePath directory) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.exists(directory.toPath()));
|
||||
}
|
||||
|
||||
static RandomAccessFile createRandomAccessFile(SafePath path) throws Exception {
|
||||
return doPrivilegedIOWithReturn(() -> new RandomAccessFile(path.toPath().toFile(), "rw"));
|
||||
}
|
||||
|
||||
public static InputStream newFileInputStream(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.newInputStream(safePath.toPath()));
|
||||
}
|
||||
|
||||
public static long getFileSize(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.size(safePath.toPath()));
|
||||
}
|
||||
|
||||
static SafePath createDirectories(SafePath safePath) throws IOException {
|
||||
Path p = doPrivilegedIOWithReturn(() -> Files.createDirectories(safePath.toPath()));
|
||||
return new SafePath(p);
|
||||
}
|
||||
|
||||
public static boolean exists(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.exists(safePath.toPath()));
|
||||
}
|
||||
|
||||
public static boolean isDirectory(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.isDirectory(safePath.toPath()));
|
||||
}
|
||||
|
||||
static void delete(SafePath localPath) throws IOException {
|
||||
doPriviligedIO(() -> Files.delete(localPath.toPath()));
|
||||
}
|
||||
|
||||
static boolean isWritable(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.isWritable(safePath.toPath()));
|
||||
}
|
||||
|
||||
static void deleteOnExit(SafePath safePath) {
|
||||
doPrivileged(() -> safePath.toPath().toFile().deleteOnExit());
|
||||
}
|
||||
|
||||
static ReadableByteChannel newFileChannelToRead(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> FileChannel.open(safePath.toPath(), StandardOpenOption.READ));
|
||||
}
|
||||
|
||||
public static InputStream getResourceAsStream(String name) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> SecuritySupport.class.getResourceAsStream(name));
|
||||
}
|
||||
|
||||
public static Reader newFileReader(SafePath safePath) throws FileNotFoundException, IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.newBufferedReader(safePath.toPath()));
|
||||
}
|
||||
|
||||
static void touch(SafePath path) throws IOException {
|
||||
doPriviligedIO(() -> new RandomAccessFile(path.toPath().toFile(), "rw").close());
|
||||
}
|
||||
|
||||
static void setAccessible(Method method) {
|
||||
doPrivileged(() -> method.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
|
||||
static void setAccessible(Field field) {
|
||||
doPrivileged(() -> field.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
|
||||
static void setAccessible(Constructor<?> constructor) {
|
||||
doPrivileged(() -> constructor.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
|
||||
static void ensureClassIsInitialized(Class<?> clazz) {
|
||||
unsafe.ensureClassInitialized(clazz);
|
||||
}
|
||||
|
||||
static Class<?> defineClass(String name, byte[] bytes, ClassLoader classLoader) {
|
||||
return unsafe.defineClass(name, bytes, 0, bytes.length, classLoader, null);
|
||||
}
|
||||
|
||||
static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
|
||||
return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]);
|
||||
}
|
||||
|
||||
static void setDaemonThread(Thread t, boolean daeomn) {
|
||||
doPrivileged(()-> t.setDaemon(daeomn), new RuntimePermission("modifyThread"));
|
||||
}
|
||||
|
||||
public static SafePath getAbsolutePath(SafePath path) throws IOException {
|
||||
return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath())));
|
||||
}
|
||||
}
|
||||
292
jdkSrc/jdk8/jdk/jfr/internal/SettingsManager.java
Normal file
292
jdkSrc/jdk8/jdk/jfr/internal/SettingsManager.java
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
|
||||
final class SettingsManager {
|
||||
|
||||
private static class InternalSetting {
|
||||
|
||||
private final String identifier;
|
||||
private Map<String, Set<String>> enabledMap = new LinkedHashMap<>(5);
|
||||
private Map<String, Set<String>> allMap = new LinkedHashMap<>(5);
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Settings identifier, for example "com.example.HelloWorld" or "56"
|
||||
* (id of event)
|
||||
*
|
||||
* @param settingsId
|
||||
*/
|
||||
public InternalSetting(String settingsId) {
|
||||
this.identifier = settingsId;
|
||||
}
|
||||
|
||||
public Set<String> getValues(String key) {
|
||||
if (enabled) {
|
||||
return enabledMap.get(key);
|
||||
} else {
|
||||
return allMap.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(String attribute, String value) {
|
||||
if ("enabled".equals(attribute) && "true".equals(value)) {
|
||||
enabled = true;
|
||||
allMap = null; // no need to keep these around
|
||||
}
|
||||
addToMap(enabledMap, attribute, value);
|
||||
if (allMap != null) {
|
||||
addToMap(allMap, attribute, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToMap(Map<String, Set<String>> map, String attribute, String value) {
|
||||
Set<String> values = map.get(attribute);
|
||||
if (values == null) {
|
||||
values = new HashSet<String>(5);
|
||||
map.put(attribute, values);
|
||||
}
|
||||
values.add(value);
|
||||
|
||||
}
|
||||
|
||||
public String getSettingsId() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void add(InternalSetting enabled) {
|
||||
for (Map.Entry<String, Set<String>> entry : enabled.enabledMap.entrySet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
add(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(identifier);
|
||||
sb.append(": ");
|
||||
sb.append(enabledMap.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
if (!enabled) {
|
||||
// settings from disabled
|
||||
// events should not impact results, but
|
||||
// we can't clear enabledMap since enabled=false
|
||||
// needs be there, so events that are enabled
|
||||
// by default are turned off
|
||||
Map<String, Set<String>> disabledMap = new HashMap<>(2);
|
||||
Set<String> values = new HashSet<>(2);
|
||||
values.add("false");
|
||||
disabledMap.put("enabled", values);
|
||||
enabledMap = disabledMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, InternalSetting> availableSettings = new LinkedHashMap<>();
|
||||
|
||||
void setSettings(List<Map<String, String>> activeSettings) {
|
||||
// store settings so they are available if a new event class is loaded
|
||||
availableSettings = createSettingsMap(activeSettings);
|
||||
List<EventControl> eventControls = MetadataRepository.getInstance().getEventControls();
|
||||
if (!JVM.getJVM().isRecording()) {
|
||||
for (EventControl ec : eventControls) {
|
||||
ec.disable();
|
||||
}
|
||||
} else {
|
||||
if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
|
||||
Collections.sort(eventControls, (x,y) -> x.getEventType().getName().compareTo(y.getEventType().getName()));
|
||||
}
|
||||
for (EventControl ec : eventControls) {
|
||||
setEventControl(ec);
|
||||
}
|
||||
}
|
||||
if (JVM.getJVM().getAllowedToDoEventRetransforms()) {
|
||||
updateRetransform(JVM.getJVM().getAllEventClasses());
|
||||
}
|
||||
}
|
||||
|
||||
public void updateRetransform(List<Class<? extends Event>> eventClasses) {
|
||||
List<Class<?>> classes = new ArrayList<>();
|
||||
for(Class<? extends Event> eventClass: eventClasses) {
|
||||
EventHandler eh = Utils.getHandler(eventClass);
|
||||
if (eh != null ) {
|
||||
PlatformEventType eventType = eh.getPlatformEventType();
|
||||
if (eventType.isMarkedForInstrumentation()) {
|
||||
classes.add(eventClass);
|
||||
eventType.markForInstrumentation(false);
|
||||
// A bit premature to set it here, but hard to check
|
||||
// after call to retransformClasses.
|
||||
eventType.setInstrumented();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!classes.isEmpty()) {
|
||||
JVM.getJVM().retransformClasses(classes.toArray(new Class<?>[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, InternalSetting> createSettingsMap(List<Map<String,String>> activeSettings) {
|
||||
Map<String, InternalSetting> map = new LinkedHashMap<>(activeSettings.size());
|
||||
for (Map<String, String> rec : activeSettings) {
|
||||
for (InternalSetting internal : makeInternalSettings(rec)) {
|
||||
InternalSetting is = map.get(internal.getSettingsId());
|
||||
if (is == null) {
|
||||
map.put(internal.getSettingsId(), internal);
|
||||
} else {
|
||||
is.add(internal);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Collection<InternalSetting> makeInternalSettings(Map<String, String> rec) {
|
||||
Map<String, InternalSetting> internals = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, String> entry : rec.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
int index = key.indexOf("#");
|
||||
if (index > 1 && index < key.length() - 2) {
|
||||
String eventName = key.substring(0, index);
|
||||
eventName = Utils.upgradeLegacyJDKEvent(eventName);
|
||||
InternalSetting s = internals.get(eventName);
|
||||
String settingName = key.substring(index + 1).trim();
|
||||
if (s == null) {
|
||||
s = new InternalSetting(eventName);
|
||||
internals.put(eventName, s);
|
||||
}
|
||||
s.add(settingName, value);
|
||||
}
|
||||
}
|
||||
for (InternalSetting s : internals.values()) {
|
||||
s.finish();
|
||||
}
|
||||
|
||||
return internals.values();
|
||||
}
|
||||
|
||||
void setEventControl(EventControl ec) {
|
||||
InternalSetting is = getInternalSetting(ec);
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
|
||||
for (Entry<String, Control> entry : ec.getEntries()) {
|
||||
Set<String> values = null;
|
||||
String settingName = entry.getKey();
|
||||
if (is != null) {
|
||||
values = is.getValues(settingName);
|
||||
}
|
||||
Control control = entry.getValue();
|
||||
if (values != null) {
|
||||
control.apply(values);
|
||||
String after = control.getLastValue();
|
||||
if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
|
||||
if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) {
|
||||
if (values.size() > 1) {
|
||||
StringJoiner sj = new StringJoiner(", ", "{", "}");
|
||||
for (String s : values) {
|
||||
sj.add("\"" + s + "\"");
|
||||
}
|
||||
String message = " " + settingName + "= " + sj.toString() + " => \"" + after + "\"";
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message);
|
||||
} else {
|
||||
String message = " " + settingName + "=\"" + control.getLastValue() + "\"";
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
control.setDefault();
|
||||
if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
|
||||
String message = " " + settingName + "=\"" + control.getLastValue() + "\"";
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
ec.writeActiveSettingEvent();
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}");
|
||||
}
|
||||
|
||||
private InternalSetting getInternalSetting(EventControl ec) {
|
||||
String name = ec.getEventType().getName();
|
||||
InternalSetting nameBased = availableSettings.get(name);
|
||||
InternalSetting idBased = availableSettings.get(ec.getSettingsId());
|
||||
|
||||
if (nameBased == null && idBased == null) {
|
||||
return null;
|
||||
}
|
||||
if (idBased == null) {
|
||||
return nameBased;
|
||||
}
|
||||
if (nameBased == null) {
|
||||
return idBased;
|
||||
}
|
||||
InternalSetting mixed = new InternalSetting(nameBased.getSettingsId());
|
||||
mixed.add(nameBased);
|
||||
mixed.add(idBased);
|
||||
return mixed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (InternalSetting enabled : availableSettings.values()) {
|
||||
sb.append(enabled.toString());
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
boolean isEnabled(String eventName) {
|
||||
InternalSetting is = availableSettings.get(eventName);
|
||||
if (is == null) {
|
||||
return false;
|
||||
}
|
||||
return is.isEnabled();
|
||||
}
|
||||
}
|
||||
105
jdkSrc/jdk8/jdk/jfr/internal/ShutdownHook.java
Normal file
105
jdkSrc/jdk8/jdk/jfr/internal/ShutdownHook.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import jdk.jfr.RecordingState;
|
||||
|
||||
/**
|
||||
* Class responsible for dumping recordings on exit
|
||||
*
|
||||
*/
|
||||
final class ShutdownHook implements Runnable {
|
||||
private final PlatformRecorder recorder;
|
||||
Object tlabDummyObject;
|
||||
|
||||
ShutdownHook(PlatformRecorder recorder) {
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// this allocation is done in order to fetch a new TLAB before
|
||||
// starting any "real" operations. In low memory situations,
|
||||
// we would like to take an OOM as early as possible.
|
||||
tlabDummyObject = new Object();
|
||||
|
||||
for (PlatformRecording recording : recorder.getRecordings()) {
|
||||
if (recording.getDumpOnExit() && recording.getState() == RecordingState.RUNNING) {
|
||||
dump(recording);
|
||||
}
|
||||
}
|
||||
recorder.destroy();
|
||||
}
|
||||
|
||||
private void dump(PlatformRecording recording) {
|
||||
try {
|
||||
WriteableUserPath dest = recording.getDestination();
|
||||
if (dest == null) {
|
||||
dest = makeDumpOnExitPath(recording);
|
||||
recording.setDestination(dest);
|
||||
}
|
||||
if (dest != null) {
|
||||
recording.stop("Dump on exit");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Could not dump recording " + recording.getName() + " on exit.");
|
||||
}
|
||||
}
|
||||
|
||||
private WriteableUserPath makeDumpOnExitPath(PlatformRecording recording) {
|
||||
try {
|
||||
String name = Utils.makeFilename(recording.getRecording());
|
||||
AccessControlContext acc = recording.getNoDestinationDumpOnExitAccessControlContext();
|
||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<WriteableUserPath>() {
|
||||
@Override
|
||||
public WriteableUserPath run() throws Exception {
|
||||
return new WriteableUserPath(recording.getDumpOnExitDirectory().toPath().resolve(name));
|
||||
}
|
||||
}, acc);
|
||||
} catch (PrivilegedActionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof SecurityException) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Not allowed to create dump path for recording " + recording.getId() + " on exit.");
|
||||
}
|
||||
if (t instanceof IOException) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not dump " + recording.getId() + " on exit.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
JVM.getJVM().uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
135
jdkSrc/jdk8/jdk/jfr/internal/StringPool.java
Normal file
135
jdkSrc/jdk8/jdk/jfr/internal/StringPool.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
public final class StringPool {
|
||||
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
static final int MIN_LIMIT = 16;
|
||||
static final int MAX_LIMIT = 128; /* 0 MAX means disabled */
|
||||
private static final long epochAddress;
|
||||
private static final SimpleStringIdPool sp = new SimpleStringIdPool();
|
||||
static {
|
||||
epochAddress = JVM.getJVM().getEpochAddress();
|
||||
sp.reset();
|
||||
}
|
||||
public static long addString(String s) {
|
||||
return sp.addString(s);
|
||||
}
|
||||
private static boolean getCurrentEpoch() {
|
||||
return unsafe.getByte(epochAddress) == 1;
|
||||
}
|
||||
private static class SimpleStringIdPool {
|
||||
/* string id index */
|
||||
private final AtomicLong sidIdx = new AtomicLong();
|
||||
/* epoch of cached strings */
|
||||
private boolean poolEpoch;
|
||||
/* the cache */
|
||||
private final ConcurrentHashMap<String, Long> cache;
|
||||
/* max size */
|
||||
private final int MAX_SIZE = 32*1024;
|
||||
/* max size bytes*/
|
||||
private final long MAX_SIZE_UTF16 = 16*1024*1024;
|
||||
/* max size bytes*/
|
||||
private long currentSizeUTF16;
|
||||
|
||||
/* looking at a biased data set 4 is a good value */
|
||||
private final String[] preCache = new String[]{"", "" , "" ,""};
|
||||
/* index of oldest */
|
||||
private int preCacheOld = 0;
|
||||
/* loop mask */
|
||||
private static final int preCacheMask = 0x03;
|
||||
|
||||
SimpleStringIdPool() {
|
||||
cache = new ConcurrentHashMap<>(MAX_SIZE, 0.75f);
|
||||
}
|
||||
void reset() {
|
||||
reset(getCurrentEpoch());
|
||||
}
|
||||
private void reset(boolean epoch) {
|
||||
this.cache.clear();
|
||||
this.poolEpoch = epoch;
|
||||
this.currentSizeUTF16 = 0;
|
||||
}
|
||||
private long addString(String s) {
|
||||
boolean currentEpoch = getCurrentEpoch();
|
||||
if (poolEpoch == currentEpoch) {
|
||||
/* pool is for current chunk */
|
||||
Long lsid = this.cache.get(s);
|
||||
if (lsid != null) {
|
||||
return lsid.longValue();
|
||||
}
|
||||
} else {
|
||||
/* pool is for an old chunk */
|
||||
reset(currentEpoch);
|
||||
}
|
||||
if (!preCache(s)) {
|
||||
/* we should not pool this string */
|
||||
return -1;
|
||||
}
|
||||
if (cache.size() > MAX_SIZE || currentSizeUTF16 > MAX_SIZE_UTF16) {
|
||||
/* pool was full */
|
||||
reset(currentEpoch);
|
||||
}
|
||||
return storeString(s);
|
||||
}
|
||||
|
||||
private long storeString(String s) {
|
||||
long sid = this.sidIdx.getAndIncrement();
|
||||
/* we can race but it is ok */
|
||||
this.cache.put(s, sid);
|
||||
boolean currentEpoch;
|
||||
synchronized(SimpleStringIdPool.class) {
|
||||
currentEpoch = JVM.addStringConstant(poolEpoch, sid, s);
|
||||
currentSizeUTF16 += s.length();
|
||||
}
|
||||
/* did we write in chunk that this pool represent */
|
||||
return currentEpoch == poolEpoch ? sid : -1;
|
||||
}
|
||||
private boolean preCache(String s) {
|
||||
if (preCache[0].equals(s)) {
|
||||
return true;
|
||||
}
|
||||
if (preCache[1].equals(s)) {
|
||||
return true;
|
||||
}
|
||||
if (preCache[2].equals(s)) {
|
||||
return true;
|
||||
}
|
||||
if (preCache[3].equals(s)) {
|
||||
return true;
|
||||
}
|
||||
preCacheOld = (preCacheOld - 1) & preCacheMask;
|
||||
preCache[preCacheOld] = s;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
326
jdkSrc/jdk8/jdk/jfr/internal/Type.java
Normal file
326
jdkSrc/jdk8/jdk/jfr/internal/Type.java
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
|
||||
/**
|
||||
* Internal data structure that describes a type,
|
||||
*
|
||||
* Used to create event types, value descriptor and annotations.
|
||||
*
|
||||
*/
|
||||
public class Type implements Comparable<Type> {
|
||||
public static final String SUPER_TYPE_ANNOTATION = java.lang.annotation.Annotation.class.getName();
|
||||
public static final String SUPER_TYPE_SETTING = SettingControl.class.getName();
|
||||
public static final String SUPER_TYPE_EVENT = Event.class.getName();
|
||||
public static final String EVENT_NAME_PREFIX = "jdk.";
|
||||
public static final String TYPES_PREFIX = "jdk.types.";
|
||||
public static final String SETTINGS_PREFIX = "jdk.settings.";
|
||||
|
||||
|
||||
// Initialization of known types
|
||||
private final static Map<Type, Class<?>> knownTypes = new HashMap<>();
|
||||
static final Type BOOLEAN = register(boolean.class, new Type("boolean", null, 4));
|
||||
static final Type CHAR = register(char.class, new Type("char", null, 5));
|
||||
static final Type FLOAT = register(float.class, new Type("float", null, 6));
|
||||
static final Type DOUBLE = register(double.class, new Type("double", null, 7));
|
||||
static final Type BYTE = register(byte.class, new Type("byte", null, 8));
|
||||
static final Type SHORT = register(short.class, new Type("short", null, 9));
|
||||
static final Type INT = register(int.class, new Type("int", null, 10));
|
||||
static final Type LONG = register(long.class, new Type("long", null, 11));
|
||||
static final Type CLASS = register(Class.class, new Type("java.lang.Class", null, 20));
|
||||
static final Type STRING = register(String.class, new Type("java.lang.String", null, 21));
|
||||
static final Type THREAD = register(Thread.class, new Type("java.lang.Thread", null, 22));
|
||||
static final Type STACK_TRACE = register(null, new Type(TYPES_PREFIX + "StackTrace", null, 23));
|
||||
|
||||
private final AnnotationConstruct annos = new AnnotationConstruct();
|
||||
private final String name;
|
||||
private final String superType;
|
||||
private final boolean constantPool;
|
||||
private final long id;
|
||||
private List<ValueDescriptor> fields = new ArrayList<>();
|
||||
private Boolean simpleType; // calculated lazy
|
||||
private boolean remove = true;
|
||||
/**
|
||||
* Creates a type
|
||||
*
|
||||
* @param javaTypeName i.e "java.lang.String"
|
||||
* @param superType i.e "java.lang.Annotation"
|
||||
* @param id the class id that represents the class in the JVM
|
||||
*
|
||||
*/
|
||||
public Type(String javaTypeName, String superType, long typeId) {
|
||||
this(javaTypeName, superType, typeId, false);
|
||||
}
|
||||
|
||||
Type(String javaTypeName, String superType, long typeId, boolean contantPool) {
|
||||
this(javaTypeName, superType, typeId, contantPool, null);
|
||||
}
|
||||
|
||||
Type(String javaTypeName, String superType, long typeId, boolean contantPool, Boolean simpleType) {
|
||||
Objects.requireNonNull(javaTypeName);
|
||||
|
||||
if (!isValidJavaIdentifier(javaTypeName)) {
|
||||
throw new IllegalArgumentException(javaTypeName + " is not a valid Java identifier");
|
||||
}
|
||||
this.constantPool = contantPool;
|
||||
this.superType = superType;
|
||||
this.name = javaTypeName;
|
||||
this.id = typeId;
|
||||
this.simpleType = simpleType;
|
||||
}
|
||||
|
||||
static boolean isDefinedByJVM(long id) {
|
||||
return id < JVM.RESERVED_CLASS_ID_LIMIT;
|
||||
}
|
||||
|
||||
public static long getTypeId(Class<?> clazz) {
|
||||
Type type = Type.getKnownType(clazz);
|
||||
return type == null ? JVM.getJVM().getTypeId(clazz) : type.getId();
|
||||
}
|
||||
|
||||
static Collection<Type> getKnownTypes() {
|
||||
return knownTypes.keySet();
|
||||
}
|
||||
|
||||
public static boolean isValidJavaIdentifier(String identifier) {
|
||||
if (identifier.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < identifier.length(); i++) {
|
||||
char c = identifier.charAt(i);
|
||||
if (c != '.') {
|
||||
if (!Character.isJavaIdentifierPart(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isValidJavaFieldType(String name) {
|
||||
for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
|
||||
Class<?> clazz = entry.getValue();
|
||||
if (clazz != null && name.equals(clazz.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Type getKnownType(String typeName) {
|
||||
for (Type type : knownTypes.keySet()) {
|
||||
if (type.getName().equals(typeName)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean isKnownType(Class<?> type) {
|
||||
if (type.isPrimitive()) {
|
||||
return true;
|
||||
}
|
||||
if (type.equals(Class.class) || type.equals(Thread.class) || type.equals(String.class)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Type getKnownType(Class<?> clazz) {
|
||||
for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
|
||||
if (clazz != null && clazz.equals(entry.getValue())) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getLogName() {
|
||||
return getName() + "(" + getId() + ")";
|
||||
}
|
||||
|
||||
public List<ValueDescriptor> getFields() {
|
||||
if (fields instanceof ArrayList) {
|
||||
((ArrayList<ValueDescriptor>) fields).trimToSize();
|
||||
fields = Collections.unmodifiableList(fields);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public boolean isSimpleType() {
|
||||
if (simpleType == null) {
|
||||
simpleType = calculateSimpleType();
|
||||
}
|
||||
return simpleType.booleanValue();
|
||||
}
|
||||
|
||||
private boolean calculateSimpleType() {
|
||||
if (fields.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
// annotation, settings and event can never be simple types
|
||||
return superType == null;
|
||||
}
|
||||
|
||||
public boolean isDefinedByJVM() {
|
||||
return id < JVM.RESERVED_CLASS_ID_LIMIT;
|
||||
}
|
||||
|
||||
private static Type register(Class<?> clazz, Type type) {
|
||||
knownTypes.put(type, clazz);
|
||||
return type;
|
||||
}
|
||||
|
||||
public void add(ValueDescriptor valueDescriptor) {
|
||||
Objects.requireNonNull(valueDescriptor);
|
||||
fields.add(valueDescriptor);
|
||||
}
|
||||
|
||||
void trimFields() {
|
||||
getFields();
|
||||
}
|
||||
|
||||
void setAnnotations(List<AnnotationElement> annotations) {
|
||||
annos.setAnnotationElements(annotations);
|
||||
}
|
||||
|
||||
public String getSuperType() {
|
||||
return superType;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isConstantPool() {
|
||||
return constantPool;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return annos.getLabel();
|
||||
}
|
||||
|
||||
public List<AnnotationElement> getAnnotationElements() {
|
||||
return annos.getUnmodifiableAnnotationElements();
|
||||
}
|
||||
|
||||
public <T> T getAnnotation(Class<? extends java.lang.annotation.Annotation> clazz) {
|
||||
return annos.getAnnotation(clazz);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return annos.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object instanceof Type) {
|
||||
Type that = (Type) object;
|
||||
return that.id == this.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Type that) {
|
||||
return Long.compare(this.id, that.id);
|
||||
}
|
||||
|
||||
void log(String action, LogTag logTag, LogLevel level) {
|
||||
if (Logger.shouldLog(logTag, level) && !isSimpleType()) {
|
||||
Logger.log(logTag, LogLevel.TRACE, action + " " + typeText() + " " + getLogName() + " {");
|
||||
for (ValueDescriptor v : getFields()) {
|
||||
String array = v.isArray() ? "[]" : "";
|
||||
Logger.log(logTag, LogLevel.TRACE, " " + v.getTypeName() + array + " " + v.getName() + ";");
|
||||
}
|
||||
Logger.log(logTag, LogLevel.TRACE, "}");
|
||||
} else {
|
||||
if (Logger.shouldLog(logTag, LogLevel.INFO) && !isSimpleType()) {
|
||||
Logger.log(logTag, LogLevel.INFO, action + " " + typeText() + " " + getLogName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String typeText() {
|
||||
if (this instanceof PlatformEventType) {
|
||||
return "event type";
|
||||
}
|
||||
if (Type.SUPER_TYPE_SETTING.equals(superType)) {
|
||||
return "setting type";
|
||||
}
|
||||
if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) {
|
||||
return "annotation type";
|
||||
}
|
||||
return "type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getLogName());
|
||||
if (!getFields().isEmpty()) {
|
||||
sb.append(" {\n");
|
||||
for (ValueDescriptor td : getFields()) {
|
||||
sb.append(" type=" + td.getTypeName() + "(" + td.getTypeId() + ") name=" + td.getName() + "\n");
|
||||
}
|
||||
sb.append("}\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setRemove(boolean remove) {
|
||||
this.remove = remove;
|
||||
}
|
||||
|
||||
public boolean getRemove() {
|
||||
return remove;
|
||||
}
|
||||
}
|
||||
492
jdkSrc/jdk8/jdk/jfr/internal/TypeLibrary.java
Normal file
492
jdkSrc/jdk8/jdk/jfr/internal/TypeLibrary.java
Normal file
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.Timestamp;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
|
||||
public final class TypeLibrary {
|
||||
|
||||
private static TypeLibrary instance;
|
||||
private static final Map<Long, Type> types = new LinkedHashMap<>(100);
|
||||
static final ValueDescriptor DURATION_FIELD = createDurationField();
|
||||
static final ValueDescriptor THREAD_FIELD = createThreadField();
|
||||
static final ValueDescriptor STACK_TRACE_FIELD = createStackTraceField();
|
||||
static final ValueDescriptor START_TIME_FIELD = createStartTimeField();
|
||||
|
||||
private TypeLibrary(List<Type> jvmTypes) {
|
||||
visitReachable(jvmTypes, t -> !types.containsKey(t.getId()), t -> types.put(t.getId(), t));
|
||||
if (Logger.shouldLog(LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO)) {
|
||||
Stream<Type> s = types.values().stream().sorted((x, y) -> Long.compare(x.getId(), y.getId()));
|
||||
s.forEach(t -> t.log("Added", LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO));
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueDescriptor createStartTimeField() {
|
||||
List<AnnotationElement> annos = createStandardAnnotations("Start Time", null);
|
||||
annos.add(new jdk.jfr.AnnotationElement(Timestamp.class, Timestamp.TICKS));
|
||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_START_TIME, Type.LONG, annos, 0, false,
|
||||
EventInstrumentation.FIELD_START_TIME);
|
||||
|
||||
}
|
||||
|
||||
private static ValueDescriptor createStackTraceField() {
|
||||
List<AnnotationElement> annos = new ArrayList<>();
|
||||
annos = createStandardAnnotations("Stack Trace", "Stack Trace starting from the method the event was committed in");
|
||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true,
|
||||
EventInstrumentation.FIELD_STACK_TRACE);
|
||||
}
|
||||
|
||||
private static ValueDescriptor createThreadField() {
|
||||
List<AnnotationElement> annos = new ArrayList<>();
|
||||
annos = createStandardAnnotations("Event Thread", "Thread in which event was committed in");
|
||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true,
|
||||
EventInstrumentation.FIELD_EVENT_THREAD);
|
||||
}
|
||||
|
||||
private static ValueDescriptor createDurationField() {
|
||||
List<AnnotationElement> annos = new ArrayList<>();
|
||||
annos = createStandardAnnotations("Duration", null);
|
||||
annos.add(new jdk.jfr.AnnotationElement(Timespan.class, Timespan.TICKS));
|
||||
return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_DURATION, Type.LONG, annos, 0, false, EventInstrumentation.FIELD_DURATION);
|
||||
}
|
||||
|
||||
public static TypeLibrary getInstance() {
|
||||
synchronized (TypeLibrary.class) {
|
||||
if (instance == null) {
|
||||
List<Type> jvmTypes;
|
||||
try {
|
||||
jvmTypes = MetadataHandler.createTypes();
|
||||
Collections.sort(jvmTypes, (a,b) -> Long.compare(a.getId(), b.getId()));
|
||||
} catch (IOException e) {
|
||||
throw new Error("JFR: Could not read metadata");
|
||||
}
|
||||
instance = new TypeLibrary(jvmTypes);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Type> getTypes() {
|
||||
return new ArrayList<>(types.values());
|
||||
}
|
||||
|
||||
public static Type createAnnotationType(Class<? extends Annotation> a) {
|
||||
if (shouldPersist(a)) {
|
||||
Type type = defineType(a, Type.SUPER_TYPE_ANNOTATION, false);
|
||||
if (type != null) {
|
||||
SecuritySupport.makeVisibleToJFR(a);
|
||||
for (Method method : a.getDeclaredMethods()) {
|
||||
type.add(PrivateAccess.getInstance().newValueDescriptor(method.getReturnType(), method.getName()));
|
||||
}
|
||||
ArrayList<AnnotationElement> aes = new ArrayList<>();
|
||||
for (Annotation annotation : resolveRepeatedAnnotations(a.getAnnotations())) {
|
||||
AnnotationElement ae = createAnnotation(annotation);
|
||||
if (ae != null) {
|
||||
aes.add(ae);
|
||||
}
|
||||
}
|
||||
aes.trimToSize();
|
||||
type.setAnnotations(aes);
|
||||
}
|
||||
return getType(a);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static AnnotationElement createAnnotation(Annotation annotation) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
Type type = createAnnotationType(annotationType);
|
||||
if (type != null) {
|
||||
List<Object> values = new ArrayList<>();
|
||||
for (ValueDescriptor v : type.getFields()) {
|
||||
values.add(invokeAnnotation(annotation, v.getName()));
|
||||
}
|
||||
|
||||
return PrivateAccess.getInstance().newAnnotation(type, values, annotation.annotationType().getClassLoader() == null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object invokeAnnotation(Annotation annotation, String methodName) {
|
||||
final Method m;
|
||||
try {
|
||||
m = annotation.getClass().getMethod(methodName, new Class<?>[0]);
|
||||
} catch (NoSuchMethodException e1) {
|
||||
throw (Error) new InternalError("Could not loacate method " + methodName + " in annotation " + annotation.getClass().getName());
|
||||
}
|
||||
SecuritySupport.setAccessible(m);
|
||||
try {
|
||||
return m.invoke(annotation, new Object[0]);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw (Error) new InternalError("Could not get value for method " + methodName + " in annotation " + annotation.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldPersist(Class<? extends Annotation> a) {
|
||||
if (a == MetadataDefinition.class || a.getAnnotation(MetadataDefinition.class) == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isDefined(Class<?> clazz) {
|
||||
return types.containsKey(Type.getTypeId(clazz));
|
||||
}
|
||||
|
||||
private static Type getType(Class<?> clazz) {
|
||||
return types.get(Type.getTypeId(clazz));
|
||||
}
|
||||
|
||||
private static Type defineType(Class<?> clazz, String superType, boolean eventType) {
|
||||
if (!isDefined(clazz)) {
|
||||
Name name = clazz.getAnnotation(Name.class);
|
||||
String typeName = name != null ? name.value() : clazz.getName();
|
||||
long id = Type.getTypeId(clazz);
|
||||
Type t;
|
||||
if (eventType) {
|
||||
t = new PlatformEventType(typeName, id, clazz.getClassLoader() == null, true);
|
||||
} else {
|
||||
t = new Type(typeName, superType, id);
|
||||
}
|
||||
types.put(t.getId(), t);
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static Type createType(Class<?> clazz) {
|
||||
return createType(clazz, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public static Type createType(Class<?> clazz, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
|
||||
|
||||
if (Thread.class == clazz) {
|
||||
return Type.THREAD;
|
||||
}
|
||||
|
||||
if (Class.class.isAssignableFrom(clazz)) {
|
||||
return Type.CLASS;
|
||||
}
|
||||
|
||||
if (String.class.equals(clazz)) {
|
||||
return Type.STRING;
|
||||
}
|
||||
|
||||
if (isDefined(clazz)) {
|
||||
return getType(clazz);
|
||||
}
|
||||
|
||||
if (clazz.isPrimitive()) {
|
||||
return defineType(clazz, null,false);
|
||||
}
|
||||
|
||||
if (clazz.isArray()) {
|
||||
throw new InternalError("Arrays not supported");
|
||||
}
|
||||
|
||||
// STRUCT
|
||||
String superType = null;
|
||||
boolean eventType = false;
|
||||
if (Event.class.isAssignableFrom(clazz)) {
|
||||
superType = Type.SUPER_TYPE_EVENT;
|
||||
eventType= true;
|
||||
}
|
||||
if (Control.class.isAssignableFrom(clazz)) {
|
||||
superType = Type.SUPER_TYPE_SETTING;
|
||||
}
|
||||
|
||||
// forward declare to avoid infinite recursion
|
||||
defineType(clazz, superType, eventType);
|
||||
Type type = getType(clazz);
|
||||
|
||||
if (eventType) {
|
||||
addImplicitFields(type, true, true, true, true ,false);
|
||||
addUserFields(clazz, type, dynamicFields);
|
||||
type.trimFields();
|
||||
}
|
||||
addAnnotations(clazz, type, dynamicAnnotations);
|
||||
|
||||
if (clazz.getClassLoader() == null) {
|
||||
type.log("Added", LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO);
|
||||
} else {
|
||||
type.log("Added", LogTag.JFR_METADATA, LogLevel.INFO);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private static void addAnnotations(Class<?> clazz, Type type, List<AnnotationElement> dynamicAnnotations) {
|
||||
ArrayList<AnnotationElement> aes = new ArrayList<>();
|
||||
if (dynamicAnnotations.isEmpty()) {
|
||||
for (Annotation a : Utils.getAnnotations(clazz)) {
|
||||
AnnotationElement ae = createAnnotation(a);
|
||||
if (ae != null) {
|
||||
aes.add(ae);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<Type> newTypes = new ArrayList<>();
|
||||
aes.addAll(dynamicAnnotations);
|
||||
for (AnnotationElement ae : dynamicAnnotations) {
|
||||
newTypes.add(PrivateAccess.getInstance().getType(ae));
|
||||
}
|
||||
addTypes(newTypes);
|
||||
}
|
||||
type.setAnnotations(aes);
|
||||
aes.trimToSize();
|
||||
}
|
||||
|
||||
private static void addUserFields(Class<?> clazz, Type type, List<ValueDescriptor> dynamicFields) {
|
||||
Map<String, ValueDescriptor> dynamicFieldSet = new HashMap<>();
|
||||
for (ValueDescriptor dynamicField : dynamicFields) {
|
||||
dynamicFieldSet.put(dynamicField.getName(), dynamicField);
|
||||
}
|
||||
List<Type> newTypes = new ArrayList<>();
|
||||
for (Field field : Utils.getVisibleEventFields(clazz)) {
|
||||
ValueDescriptor vd = dynamicFieldSet.get(field.getName());
|
||||
if (vd != null) {
|
||||
if (!vd.getTypeName().equals(field.getType().getName())) {
|
||||
throw new InternalError("Type expected to match for field " + vd.getName() + " expected " + field.getName() + " but got " + vd.getName());
|
||||
}
|
||||
for (AnnotationElement ae : vd.getAnnotationElements()) {
|
||||
newTypes.add(PrivateAccess.getInstance().getType(ae));
|
||||
}
|
||||
newTypes.add(PrivateAccess.getInstance().getType(vd));
|
||||
} else {
|
||||
vd = createField(field);
|
||||
}
|
||||
if (vd != null) {
|
||||
type.add(vd);
|
||||
}
|
||||
}
|
||||
addTypes(newTypes);
|
||||
}
|
||||
|
||||
// By convention all events have these fields.
|
||||
static void addImplicitFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) {
|
||||
createAnnotationType(Timespan.class);
|
||||
createAnnotationType(Timestamp.class);
|
||||
createAnnotationType(Label.class);
|
||||
defineType(long.class, null,false);
|
||||
addFields(type, requestable, hasDuration, hasThread, hasStackTrace, hasCutoff);
|
||||
}
|
||||
|
||||
private static void addFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) {
|
||||
type.add(START_TIME_FIELD);
|
||||
if (hasDuration || hasCutoff) {
|
||||
type.add(DURATION_FIELD);
|
||||
}
|
||||
if (hasThread) {
|
||||
type.add(THREAD_FIELD);
|
||||
}
|
||||
if (hasStackTrace) {
|
||||
type.add(STACK_TRACE_FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<AnnotationElement> createStandardAnnotations(String name, String description) {
|
||||
List<AnnotationElement> annotationElements = new ArrayList<>(2);
|
||||
annotationElements.add(new jdk.jfr.AnnotationElement(Label.class, name));
|
||||
if (description != null) {
|
||||
annotationElements.add(new jdk.jfr.AnnotationElement(Description.class, description));
|
||||
}
|
||||
return annotationElements;
|
||||
}
|
||||
|
||||
private static ValueDescriptor createField(Field field) {
|
||||
int mod = field.getModifiers();
|
||||
if (Modifier.isTransient(mod)) {
|
||||
return null;
|
||||
}
|
||||
if (Modifier.isStatic(mod)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> fieldType = field.getType();
|
||||
if (!Type.isKnownType(fieldType)) {
|
||||
return null;
|
||||
}
|
||||
boolean constantPool = Thread.class == fieldType || fieldType == Class.class;
|
||||
Type type = createType(fieldType);
|
||||
String fieldName = field.getName();
|
||||
Name name = field.getAnnotation(Name.class);
|
||||
String useName = fieldName;
|
||||
if (name != null) {
|
||||
useName = name.value();
|
||||
}
|
||||
List<jdk.jfr.AnnotationElement> ans = new ArrayList<>();
|
||||
for (Annotation a : resolveRepeatedAnnotations(field.getAnnotations())) {
|
||||
AnnotationElement ae = createAnnotation(a);
|
||||
if (ae != null) {
|
||||
ans.add(ae);
|
||||
}
|
||||
}
|
||||
return PrivateAccess.getInstance().newValueDescriptor(useName, type, ans, 0, constantPool, fieldName);
|
||||
}
|
||||
|
||||
private static List<Annotation> resolveRepeatedAnnotations(Annotation[] annotations) {
|
||||
List<Annotation> annos = new ArrayList<>(annotations.length);
|
||||
for (Annotation a : annotations) {
|
||||
boolean repeated = false;
|
||||
Method m;
|
||||
try {
|
||||
m = a.annotationType().getMethod("value");
|
||||
Class<?> returnType = m.getReturnType();
|
||||
if (returnType.isArray()) {
|
||||
Class<?> ct = returnType.getComponentType();
|
||||
if (Annotation.class.isAssignableFrom(ct) && ct.getAnnotation(Repeatable.class) != null) {
|
||||
Object res = m.invoke(a, new Object[0]);
|
||||
if (res != null && Annotation[].class.isAssignableFrom(res.getClass())) {
|
||||
for (Annotation rep : (Annotation[]) m.invoke(a, new Object[0])) {
|
||||
annos.add(rep);
|
||||
}
|
||||
repeated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
// Ignore, can't access repeatable information
|
||||
}
|
||||
if (!repeated) {
|
||||
annos.add(a);
|
||||
}
|
||||
}
|
||||
return annos;
|
||||
}
|
||||
|
||||
// Purpose of this method is to mark types that are reachable
|
||||
// from registered event types. Those types that are not reachable can
|
||||
// safely be removed
|
||||
public boolean clearUnregistered() {
|
||||
Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Cleaning out obsolete metadata");
|
||||
List<Type> registered = new ArrayList<>();
|
||||
for (Type type : types.values()) {
|
||||
if (type instanceof PlatformEventType) {
|
||||
if (((PlatformEventType) type).isRegistered()) {
|
||||
registered.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
visitReachable(registered, t -> t.getRemove(), t -> t.setRemove(false));
|
||||
List<Long> removeIds = new ArrayList<>();
|
||||
for (Type type : types.values()) {
|
||||
if (type.getRemove() && !Type.isDefinedByJVM(type.getId())) {
|
||||
removeIds.add(type.getId());
|
||||
if (Logger.shouldLog(LogTag.JFR_METADATA, LogLevel.TRACE)) {
|
||||
Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Removed obsolete metadata " + type.getName());
|
||||
}
|
||||
}
|
||||
// Optimization, set to true now to avoid iterating
|
||||
// types first thing at next call to clearUnregistered
|
||||
type.setRemove(true);
|
||||
}
|
||||
for (Long id : removeIds) {
|
||||
types.remove(id);
|
||||
}
|
||||
return !removeIds.isEmpty();
|
||||
}
|
||||
|
||||
public void addType(Type type) {
|
||||
addTypes(Collections.singletonList(type));
|
||||
}
|
||||
|
||||
public static void addTypes(List<Type> ts) {
|
||||
if (!ts.isEmpty()) {
|
||||
visitReachable(ts, t -> !types.containsKey(t.getId()), t -> types.put(t.getId(), t));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates all reachable types from a start collection
|
||||
*
|
||||
* @param rootSet the types to start from
|
||||
* @param p if a type should be accepted
|
||||
* @param c action to take on an accepted type
|
||||
*/
|
||||
private static void visitReachable(Collection<Type> rootSet, Predicate<Type> p, Consumer<Type> c) {
|
||||
Queue<Type> typeQ = new ArrayDeque<>(rootSet);
|
||||
while (!typeQ.isEmpty()) {
|
||||
Type type = typeQ.poll();
|
||||
if (p.test(type)) {
|
||||
c.accept(type);
|
||||
visitAnnotations(typeQ, type.getAnnotationElements());
|
||||
for (ValueDescriptor v : type.getFields()) {
|
||||
typeQ.add(PrivateAccess.getInstance().getType(v));
|
||||
visitAnnotations(typeQ, v.getAnnotationElements());
|
||||
}
|
||||
if (type instanceof PlatformEventType) {
|
||||
PlatformEventType pe = (PlatformEventType) type;
|
||||
for (SettingDescriptor s : pe.getAllSettings()) {
|
||||
typeQ.add(PrivateAccess.getInstance().getType(s));
|
||||
visitAnnotations(typeQ, s.getAnnotationElements());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void visitAnnotations(Queue<Type> typeQ, List<AnnotationElement> aes) {
|
||||
Queue<AnnotationElement> aQ = new ArrayDeque<>(aes);
|
||||
Set<AnnotationElement> visited = new HashSet<>();
|
||||
while (!aQ.isEmpty()) {
|
||||
AnnotationElement ae = aQ.poll();
|
||||
if (!visited.contains(ae)) {
|
||||
Type ty = PrivateAccess.getInstance().getType(ae);
|
||||
typeQ.add(ty);
|
||||
visited.add(ae);
|
||||
}
|
||||
aQ.addAll(ae.getAnnotationElements());
|
||||
}
|
||||
}
|
||||
}
|
||||
554
jdkSrc/jdk8/jdk/jfr/internal/Utils.java
Normal file
554
jdkSrc/jdk8/jdk/jfr/internal/Utils.java
Normal file
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MICROSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.FlightRecorderPermission;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.handlers.EventHandler;
|
||||
import jdk.jfr.internal.settings.PeriodSetting;
|
||||
import jdk.jfr.internal.settings.StackTraceSetting;
|
||||
import jdk.jfr.internal.settings.ThresholdSetting;
|
||||
|
||||
public final class Utils {
|
||||
|
||||
private static final String INFINITY = "infinity";
|
||||
|
||||
private static Boolean SAVE_GENERATED;
|
||||
|
||||
public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
|
||||
public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument";
|
||||
public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers";
|
||||
public static final String REGISTER_EVENT = "registerEvent";
|
||||
public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
|
||||
|
||||
private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
|
||||
|
||||
public static void checkAccessFlightRecorder() throws SecurityException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new FlightRecorderPermission(ACCESS_FLIGHT_RECORDER));
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkRegisterPermission() throws SecurityException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new FlightRecorderPermission(REGISTER_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
private static enum TimespanUnit {
|
||||
NANOSECONDS("ns", 1000), MICROSECONDS("us", 1000), MILLISECONDS("ms", 1000), SECONDS("s", 60), MINUTES("m", 60), HOURS("h", 24), DAYS("d", 7);
|
||||
|
||||
final String text;
|
||||
final long amount;
|
||||
|
||||
TimespanUnit(String unit, long amount) {
|
||||
this.text = unit;
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
|
||||
// Tjis method can't handle Long.MIN_VALUE because absolute value is negative
|
||||
private static String formatDataAmount(String formatter, long amount) {
|
||||
int exp = (int) (Math.log(Math.abs(amount)) / Math.log(1024));
|
||||
char unitPrefix = "kMGTPE".charAt(exp - 1);
|
||||
return String.format(formatter, amount / Math.pow(1024, exp), unitPrefix);
|
||||
}
|
||||
|
||||
public static String formatBytesCompact(long bytes) {
|
||||
if (bytes < 1024) {
|
||||
return String.valueOf(bytes);
|
||||
}
|
||||
return formatDataAmount("%.1f%cB", bytes);
|
||||
}
|
||||
|
||||
public static String formatBits(long bits) {
|
||||
if (bits == 1 || bits == -1) {
|
||||
return bits + " bit";
|
||||
}
|
||||
if (bits < 1024 && bits > -1024) {
|
||||
return bits + " bits";
|
||||
}
|
||||
return formatDataAmount("%.1f %cbit", bits);
|
||||
}
|
||||
|
||||
public static String formatBytes(long bytes) {
|
||||
if (bytes == 1 || bytes == -1) {
|
||||
return bytes + " byte";
|
||||
}
|
||||
if (bytes < 1024 && bytes > -1024) {
|
||||
return bytes + " bytes";
|
||||
}
|
||||
return formatDataAmount("%.1f %cB", bytes);
|
||||
}
|
||||
|
||||
public static String formatBytesPerSecond(long bytes) {
|
||||
if (bytes < 1024 && bytes > -1024) {
|
||||
return bytes + " byte/s";
|
||||
}
|
||||
return formatDataAmount("%.1f %cB/s", bytes);
|
||||
}
|
||||
|
||||
public static String formatBitsPerSecond(long bits) {
|
||||
if (bits < 1024 && bits > -1024) {
|
||||
return bits + " bps";
|
||||
}
|
||||
return formatDataAmount("%.1f %cbps", bits);
|
||||
}
|
||||
public static String formatTimespan(Duration dValue, String separation) {
|
||||
if (dValue == null) {
|
||||
return "0";
|
||||
}
|
||||
long value = dValue.toNanos();
|
||||
TimespanUnit result = TimespanUnit.NANOSECONDS;
|
||||
for (TimespanUnit unit : TimespanUnit.values()) {
|
||||
result = unit;
|
||||
long amount = unit.amount;
|
||||
if (result == TimespanUnit.DAYS || value < amount || value % amount != 0) {
|
||||
break;
|
||||
}
|
||||
value /= amount;
|
||||
}
|
||||
return String.format("%d%s%s", value, separation, result.text);
|
||||
}
|
||||
|
||||
public static long parseTimespanWithInfinity(String s) {
|
||||
if (INFINITY.equals(s)) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
return parseTimespan(s);
|
||||
}
|
||||
|
||||
public static long parseTimespan(String s) {
|
||||
if (s.endsWith("ns")) {
|
||||
return Long.parseLong(s.substring(0, s.length() - 2).trim());
|
||||
}
|
||||
if (s.endsWith("us")) {
|
||||
return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MICROSECONDS);
|
||||
}
|
||||
if (s.endsWith("ms")) {
|
||||
return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MILLISECONDS);
|
||||
}
|
||||
if (s.endsWith("s")) {
|
||||
return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
||||
}
|
||||
if (s.endsWith("m")) {
|
||||
return 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
||||
}
|
||||
if (s.endsWith("h")) {
|
||||
return 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
||||
}
|
||||
if (s.endsWith("d")) {
|
||||
return 24 * 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS);
|
||||
}
|
||||
|
||||
try {
|
||||
Long.parseLong(s);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new NumberFormatException("'" + s + "' is not a valid timespan. Shoule be numeric value followed by a unit, i.e. 20 ms. Valid units are ns, us, s, m, h and d.");
|
||||
}
|
||||
// Only accept values with units
|
||||
throw new NumberFormatException("Timespan + '" + s + "' is missing unit. Valid units are ns, us, s, m, h and d.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all annotations as they are visible in the source code
|
||||
*
|
||||
* @param clazz class to return annotations from
|
||||
*
|
||||
* @return list of annotation
|
||||
*
|
||||
*/
|
||||
static List<Annotation> getAnnotations(Class<?> clazz) {
|
||||
List<Annotation> annos = new ArrayList<>();
|
||||
for (Annotation a : clazz.getAnnotations()) {
|
||||
annos.addAll(getAnnotation(a));
|
||||
}
|
||||
return annos;
|
||||
}
|
||||
|
||||
private static List<? extends Annotation> getAnnotation(Annotation a) {
|
||||
Class<?> annotated = a.annotationType();
|
||||
Method valueMethod = getValueMethod(annotated);
|
||||
if (valueMethod != null) {
|
||||
Class<?> returnType = valueMethod.getReturnType();
|
||||
if (returnType.isArray()) {
|
||||
Class<?> candidate = returnType.getComponentType();
|
||||
Repeatable r = candidate.getAnnotation(Repeatable.class);
|
||||
if (r != null) {
|
||||
Class<?> repeatClass = r.value();
|
||||
if (annotated == repeatClass) {
|
||||
return getAnnotationValues(a, valueMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Annotation> annos = new ArrayList<>();
|
||||
annos.add(a);
|
||||
return annos;
|
||||
}
|
||||
|
||||
static boolean isAfter(RecordingState stateToTest, RecordingState b) {
|
||||
return stateToTest.ordinal() > b.ordinal();
|
||||
}
|
||||
|
||||
static boolean isBefore(RecordingState stateToTest, RecordingState b) {
|
||||
return stateToTest.ordinal() < b.ordinal();
|
||||
}
|
||||
|
||||
static boolean isState(RecordingState stateToTest, RecordingState... states) {
|
||||
for (RecordingState s : states) {
|
||||
if (s == stateToTest) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<Annotation> getAnnotationValues(Annotation a, Method valueMethod) {
|
||||
try {
|
||||
return Arrays.asList((Annotation[]) valueMethod.invoke(a, new Object[0]));
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private static Method getValueMethod(Class<?> annotated) {
|
||||
try {
|
||||
return annotated.getMethod("value", new Class<?>[0]);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void touch(Path dumpFile) throws IOException {
|
||||
RandomAccessFile raf = new RandomAccessFile(dumpFile.toFile(), "rw");
|
||||
raf.close();
|
||||
}
|
||||
|
||||
public static Class<?> unboxType(Class<?> t) {
|
||||
if (t == Integer.class) {
|
||||
return int.class;
|
||||
}
|
||||
if (t == Long.class) {
|
||||
return long.class;
|
||||
}
|
||||
if (t == Float.class) {
|
||||
return float.class;
|
||||
}
|
||||
if (t == Double.class) {
|
||||
return double.class;
|
||||
}
|
||||
if (t == Byte.class) {
|
||||
return byte.class;
|
||||
}
|
||||
if (t == Short.class) {
|
||||
return short.class;
|
||||
}
|
||||
if (t == Boolean.class) {
|
||||
return boolean.class;
|
||||
}
|
||||
if (t == Character.class) {
|
||||
return char.class;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static long nanosToTicks(long nanos) {
|
||||
return (long) (nanos * JVM.getJVM().getTimeConversionFactor());
|
||||
}
|
||||
|
||||
static synchronized EventHandler getHandler(Class<? extends Event> eventClass) {
|
||||
Utils.ensureValidEventSubclass(eventClass);
|
||||
try {
|
||||
Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
|
||||
SecuritySupport.setAccessible(f);
|
||||
return (EventHandler) f.get(null);
|
||||
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new InternalError("Could not access event handler");
|
||||
}
|
||||
}
|
||||
|
||||
static synchronized void setHandler(Class<? extends Event> eventClass, EventHandler handler) {
|
||||
Utils.ensureValidEventSubclass(eventClass);
|
||||
try {
|
||||
Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER);
|
||||
SecuritySupport.setAccessible(field);
|
||||
field.set(null, handler);
|
||||
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new InternalError("Could not access event handler");
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> sanitizeNullFreeStringMap(Map<String, String> settings) {
|
||||
HashMap<String, String> map = new HashMap<>(settings.size());
|
||||
for (Map.Entry<String, String> e : settings.entrySet()) {
|
||||
String key = e.getKey();
|
||||
if (key == null) {
|
||||
throw new NullPointerException("Null key is not allowed in map");
|
||||
}
|
||||
String value = e.getValue();
|
||||
if (value == null) {
|
||||
throw new NullPointerException("Null value is not allowed in map");
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static <T> List<T> sanitizeNullFreeList(List<T> elements, Class<T> clazz) {
|
||||
List<T> sanitized = new ArrayList<>(elements.size());
|
||||
for (T element : elements) {
|
||||
if (element == null) {
|
||||
throw new NullPointerException("Null is not an allowed element in list");
|
||||
}
|
||||
if (element.getClass() != clazz) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
sanitized.add(element);
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
static List<Field> getVisibleEventFields(Class<?> clazz) {
|
||||
Utils.ensureValidEventSubclass(clazz);
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Class<?> c = clazz; c != Event.class; c = c.getSuperclass()) {
|
||||
for (Field field : c.getDeclaredFields()) {
|
||||
// skip private field in base classes
|
||||
if (c == clazz || !Modifier.isPrivate(field.getModifiers())) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public static void ensureValidEventSubclass(Class<?> eventClass) {
|
||||
if (Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) {
|
||||
throw new IllegalArgumentException("Abstract event classes are not allowed");
|
||||
}
|
||||
if (eventClass == Event.class || !Event.class.isAssignableFrom(eventClass)) {
|
||||
throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeGeneratedASM(String className, byte[] bytes) {
|
||||
if (SAVE_GENERATED == null) {
|
||||
// We can't calculate value statically because it will force
|
||||
// initialization of SecuritySupport, which cause
|
||||
// UnsatisfiedLinkedError on JDK 8 or non-Oracle JDKs
|
||||
SAVE_GENERATED = SecuritySupport.getBooleanProperty("jfr.save.generated.asm");
|
||||
}
|
||||
if (SAVE_GENERATED) {
|
||||
try {
|
||||
try (FileOutputStream fos = new FileOutputStream(className + ".class")) {
|
||||
fos.write(bytes);
|
||||
}
|
||||
|
||||
try (FileWriter fw = new FileWriter(className + ".asm"); PrintWriter pw = new PrintWriter(fw)) {
|
||||
ClassReader cr = new ClassReader(bytes);
|
||||
CheckClassAdapter.verify(cr, true, pw);
|
||||
}
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Instrumented code saved to " + className + ".class and .asm");
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Could not save instrumented code, for " + className + ".class and .asm");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureInitialized(Class<? extends Event> eventClass) {
|
||||
SecuritySupport.ensureClassIsInitialized(eventClass);
|
||||
}
|
||||
|
||||
public static Object makePrimitiveArray(String typeName, List<Object> values) {
|
||||
int length = values.size();
|
||||
switch (typeName) {
|
||||
case "int":
|
||||
int[] ints = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
ints[i] = (int) values.get(i);
|
||||
}
|
||||
return ints;
|
||||
case "long":
|
||||
long[] longs = new long[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
longs[i] = (long) values.get(i);
|
||||
}
|
||||
return longs;
|
||||
|
||||
case "float":
|
||||
float[] floats = new float[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
floats[i] = (float) values.get(i);
|
||||
}
|
||||
return floats;
|
||||
|
||||
case "double":
|
||||
double[] doubles = new double[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
doubles[i] = (double) values.get(i);
|
||||
}
|
||||
return doubles;
|
||||
|
||||
case "short":
|
||||
short[] shorts = new short[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
shorts[i] = (short) values.get(i);
|
||||
}
|
||||
return shorts;
|
||||
case "char":
|
||||
char[] chars = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
chars[i] = (char) values.get(i);
|
||||
}
|
||||
return chars;
|
||||
case "byte":
|
||||
byte[] bytes = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
bytes[i] = (byte) values.get(i);
|
||||
}
|
||||
return bytes;
|
||||
case "boolean":
|
||||
boolean[] booleans = new boolean[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
booleans[i] = (boolean) values.get(i);
|
||||
}
|
||||
return booleans;
|
||||
case "java.lang.String":
|
||||
String[] strings = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
strings[i] = (String) values.get(i);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isSettingVisible(Control c, boolean hasEventHook) {
|
||||
if (c instanceof ThresholdSetting) {
|
||||
return !hasEventHook;
|
||||
}
|
||||
if (c instanceof PeriodSetting) {
|
||||
return hasEventHook;
|
||||
}
|
||||
if (c instanceof StackTraceSetting) {
|
||||
return !hasEventHook;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isSettingVisible(long typeId, boolean hasEventHook) {
|
||||
if (ThresholdSetting.isType(typeId)) {
|
||||
return !hasEventHook;
|
||||
}
|
||||
if (PeriodSetting.isType(typeId)) {
|
||||
return hasEventHook;
|
||||
}
|
||||
if (StackTraceSetting.isType(typeId)) {
|
||||
return !hasEventHook;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Type getValidType(Class<?> type, String name) {
|
||||
Objects.requireNonNull(type, "Null is not a valid type for value descriptor " + name);
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
if (type != String.class && !type.isPrimitive()) {
|
||||
throw new IllegalArgumentException("Only arrays of primitives and Strings are allowed");
|
||||
}
|
||||
}
|
||||
|
||||
Type knownType = Type.getKnownType(type);
|
||||
if (knownType == null || knownType == Type.STACK_TRACE) {
|
||||
throw new IllegalArgumentException("Only primitive types, java.lang.Thread, java.lang.String and java.lang.Class are allowed for value descriptors. " + type.getName());
|
||||
}
|
||||
return knownType;
|
||||
}
|
||||
|
||||
public static <T> List<T> smallUnmodifiable(List<T> list) {
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (list.size() == 1) {
|
||||
return Collections.singletonList(list.get(0));
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
public static String upgradeLegacyJDKEvent(String eventName) {
|
||||
if (eventName.length() <= LEGACY_EVENT_NAME_PREFIX.length()) {
|
||||
return eventName;
|
||||
}
|
||||
if (eventName.startsWith(LEGACY_EVENT_NAME_PREFIX)) {
|
||||
int index = eventName.lastIndexOf(".");
|
||||
if (index == LEGACY_EVENT_NAME_PREFIX.length() - 1) {
|
||||
return Type.EVENT_NAME_PREFIX + eventName.substring(index + 1);
|
||||
}
|
||||
}
|
||||
return eventName;
|
||||
}
|
||||
|
||||
public static String makeFilename(Recording recording) {
|
||||
String pid = JVM.getJVM().getPid();
|
||||
String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now());
|
||||
String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId());
|
||||
return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
|
||||
}
|
||||
}
|
||||
141
jdkSrc/jdk8/jdk/jfr/internal/WriteableUserPath.java
Normal file
141
jdkSrc/jdk8/jdk/jfr/internal/WriteableUserPath.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Purpose of this class is to simplify analysis of security risks.
|
||||
* <p>
|
||||
* Paths in the public API should be wrapped in this class so we
|
||||
* at all time know what kind of paths we are dealing with.
|
||||
* <p>
|
||||
* A user supplied path must never be used in an unsafe context, such as a
|
||||
* shutdown hook or any other thread created by JFR.
|
||||
* <p>
|
||||
* All operation using this path must happen in {@link #doPriviligedIO(Callable)}
|
||||
*/
|
||||
public final class WriteableUserPath {
|
||||
private final AccessControlContext controlContext;
|
||||
private final Path original;
|
||||
private final Path real;
|
||||
private final String realPathText;
|
||||
private final String originalText;
|
||||
|
||||
// Not to ensure security, but to help
|
||||
// against programming errors
|
||||
private volatile boolean inPrivileged;
|
||||
|
||||
public WriteableUserPath(Path path) throws IOException {
|
||||
controlContext = AccessController.getContext();
|
||||
// verify that the path is writeable
|
||||
if (Files.exists(path) && !Files.isWritable(path)) {
|
||||
// throw same type of exception as FileOutputStream
|
||||
// constructor, if file can't be opened.
|
||||
throw new FileNotFoundException("Could not write to file: " + path.toAbsolutePath());
|
||||
}
|
||||
// will throw if non-writeable
|
||||
BufferedWriter fw = Files.newBufferedWriter(path);
|
||||
fw.close();
|
||||
this.original = path;
|
||||
this.originalText = path.toString();
|
||||
this.real = path.toRealPath();
|
||||
this.realPathText = real.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a potentially malicious path where the user may have implemented
|
||||
* their own version of Path. This method should never be called in an
|
||||
* unsafe context and the Path value should never be passed along to other
|
||||
* methods.
|
||||
*
|
||||
* @return path from a potentially malicious user
|
||||
*/
|
||||
public Path getPotentiallyMaliciousOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the real path.
|
||||
*
|
||||
* @return path as text
|
||||
*/
|
||||
public String getRealPathText() {
|
||||
return realPathText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the original path.
|
||||
*
|
||||
* @return path as text
|
||||
*/
|
||||
public String getOriginalText() {
|
||||
return originalText;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a potentially malicious path where the user may have implemented
|
||||
* their own version of Path. This method should never be called in an
|
||||
* unsafe context and the Path value should never be passed along to other
|
||||
* methods.
|
||||
*
|
||||
* @return path from a potentially malicious user
|
||||
*/
|
||||
public Path getReal() {
|
||||
if (!inPrivileged) {
|
||||
throw new InternalError("A user path was accessed outside the context it was supplied in");
|
||||
}
|
||||
return real;
|
||||
}
|
||||
|
||||
public void doPriviligedIO(Callable<?> function) throws IOException {
|
||||
try {
|
||||
inPrivileged = true;
|
||||
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
function.call();
|
||||
return null;
|
||||
}
|
||||
}, controlContext);
|
||||
} catch (Throwable t) {
|
||||
// prevent malicious user to propagate exception callback
|
||||
// in the wrong context
|
||||
throw new IOException("Unexpected error during I/O operation");
|
||||
} finally {
|
||||
inPrivileged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
184
jdkSrc/jdk8/jdk/jfr/internal/consumer/ChunkHeader.java
Normal file
184
jdkSrc/jdk8/jdk/jfr/internal/consumer/ChunkHeader.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.MetadataDescriptor;
|
||||
|
||||
public final class ChunkHeader {
|
||||
private static final long METADATA_TYPE_ID = 0;
|
||||
private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
|
||||
|
||||
private final short major;
|
||||
private final short minor;
|
||||
private final long chunkSize;
|
||||
private final long chunkStartTicks;
|
||||
private final long ticksPerSecond;
|
||||
private final long chunkStartNanos;
|
||||
private final long metadataPosition;
|
||||
// private final long absoluteInitialConstantPoolPosition;
|
||||
private final long absoluteChunkEnd;
|
||||
private final long absoluteEventStart;
|
||||
private final long absoluteChunkStart;
|
||||
private final boolean lastChunk;
|
||||
private final RecordingInput input;
|
||||
private final long durationNanos;
|
||||
private final long id;
|
||||
private long constantPoolPosition;
|
||||
|
||||
public ChunkHeader(RecordingInput input) throws IOException {
|
||||
this(input, 0, 0);
|
||||
}
|
||||
|
||||
private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
|
||||
input.position(absoluteChunkStart);
|
||||
if (input.position() >= input.size()) {
|
||||
throw new IOException("Chunk contains no data");
|
||||
}
|
||||
verifyMagic(input);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id);
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
|
||||
major = input.readRawShort();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
|
||||
minor = input.readRawShort();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor);
|
||||
if (major != 1 && major != 2) {
|
||||
throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
|
||||
}
|
||||
chunkSize = input.readRawLong();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
|
||||
this.constantPoolPosition = input.readRawLong();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
|
||||
metadataPosition = input.readRawLong();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
|
||||
chunkStartNanos = input.readRawLong(); // nanos since epoch
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
|
||||
durationNanos = input.readRawLong(); // duration nanos, not used
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos);
|
||||
chunkStartTicks = input.readRawLong();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks);
|
||||
ticksPerSecond = input.readRawLong();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
|
||||
input.readRawInt(); // features, not used
|
||||
|
||||
// set up boundaries
|
||||
this.absoluteChunkStart = absoluteChunkStart;
|
||||
absoluteChunkEnd = absoluteChunkStart + chunkSize;
|
||||
lastChunk = input.size() == absoluteChunkEnd;
|
||||
absoluteEventStart = input.position();
|
||||
|
||||
// read metadata
|
||||
input.position(absoluteEventStart);
|
||||
}
|
||||
|
||||
public ChunkHeader nextHeader() throws IOException {
|
||||
return new ChunkHeader(input, absoluteChunkEnd, id + 1);
|
||||
}
|
||||
|
||||
public MetadataDescriptor readMetadata() throws IOException {
|
||||
input.position(absoluteChunkStart + metadataPosition);
|
||||
input.readInt(); // size
|
||||
long id = input.readLong(); // event type id
|
||||
if (id != METADATA_TYPE_ID) {
|
||||
throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID);
|
||||
}
|
||||
input.readLong(); // start time
|
||||
input.readLong(); // duration
|
||||
long metadataId = input.readLong();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId);
|
||||
// No need to read if metadataId == lastMetadataId, but we
|
||||
// do it for verification purposes.
|
||||
return MetadataDescriptor.read(input);
|
||||
}
|
||||
|
||||
public boolean isLastChunk() {
|
||||
return lastChunk;
|
||||
}
|
||||
|
||||
public short getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
public short getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
public long getAbsoluteChunkStart() {
|
||||
return absoluteChunkStart;
|
||||
}
|
||||
|
||||
public long getConstantPoolPosition() {
|
||||
return constantPoolPosition;
|
||||
}
|
||||
|
||||
public long getStartTicks() {
|
||||
return chunkStartTicks;
|
||||
}
|
||||
|
||||
public double getTicksPerSecond() {
|
||||
return ticksPerSecond;
|
||||
}
|
||||
|
||||
public long getStartNanos() {
|
||||
return chunkStartNanos;
|
||||
}
|
||||
|
||||
public long getEnd() {
|
||||
return absoluteChunkEnd;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return chunkSize;
|
||||
}
|
||||
|
||||
public long getDurationNanos() {
|
||||
return durationNanos;
|
||||
}
|
||||
|
||||
public RecordingInput getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
private static void verifyMagic(DataInput input) throws IOException {
|
||||
for (byte c : FILE_MAGIC) {
|
||||
if (input.readByte() != c) {
|
||||
throw new IOException("Not a Flight Recorder file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getEventStart() {
|
||||
return absoluteEventStart;
|
||||
}
|
||||
|
||||
}
|
||||
349
jdkSrc/jdk8/jdk/jfr/internal/consumer/RecordingInput.java
Normal file
349
jdkSrc/jdk8/jdk/jfr/internal/consumer/RecordingInput.java
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, 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.DataInput;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
|
||||
public static final byte STRING_ENCODING_NULL = 0;
|
||||
public static final byte STRING_ENCODING_EMPTY_STRING = 1;
|
||||
public static final byte STRING_ENCODING_CONSTANT_POOL = 2;
|
||||
public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3;
|
||||
public static final byte STRING_ENCODING_CHAR_ARRAY = 4;
|
||||
public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5;
|
||||
|
||||
private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024;
|
||||
private final static Charset UTF8 = Charset.forName("UTF-8");
|
||||
private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
private static final class Block {
|
||||
private byte[] bytes = new byte[0];
|
||||
private long blockPosition;
|
||||
|
||||
boolean contains(long position) {
|
||||
return position >= blockPosition && position < blockPosition + bytes.length;
|
||||
}
|
||||
|
||||
public void read(RandomAccessFile file, int amount) throws IOException {
|
||||
blockPosition = file.getFilePointer();
|
||||
// reuse byte array, if possible
|
||||
if (amount != bytes.length) {
|
||||
bytes = new byte[amount];
|
||||
}
|
||||
file.readFully(bytes);
|
||||
}
|
||||
|
||||
public byte get(long position) {
|
||||
return bytes[(int) (position - blockPosition)];
|
||||
}
|
||||
}
|
||||
|
||||
private final RandomAccessFile file;
|
||||
private final long size;
|
||||
private Block currentBlock = new Block();
|
||||
private Block previousBlock = new Block();
|
||||
private long position;
|
||||
private final int blockSize;
|
||||
|
||||
private RecordingInput(File f, int blockSize) throws IOException {
|
||||
this.size = f.length();
|
||||
this.blockSize = blockSize;
|
||||
this.file = new RandomAccessFile(f, "r");
|
||||
if (size < 8) {
|
||||
throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
public RecordingInput(File f) throws IOException {
|
||||
this(f, DEFAULT_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte readByte() throws IOException {
|
||||
if (!currentBlock.contains(position)) {
|
||||
position(position);
|
||||
}
|
||||
return currentBlock.get(position++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void readFully(byte[] dest, int offset, int length) throws IOException {
|
||||
// TODO: Optimize, use Arrays.copy if all bytes are in current block
|
||||
// array
|
||||
for (int i = 0; i < length; i++) {
|
||||
dest[i + offset] = readByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void readFully(byte[] dst) throws IOException {
|
||||
readFully(dst, 0, dst.length);
|
||||
}
|
||||
|
||||
public final short readRawShort() throws IOException {
|
||||
// copied from java.io.Bits
|
||||
byte b0 = readByte();
|
||||
byte b1 = readByte();
|
||||
return (short) ((b1 & 0xFF) + (b0 << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double readDouble() throws IOException {
|
||||
// copied from java.io.Bits
|
||||
return Double.longBitsToDouble(readRawLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final float readFloat() throws IOException {
|
||||
// copied from java.io.Bits
|
||||
return Float.intBitsToFloat(readRawInt());
|
||||
}
|
||||
|
||||
public final int readRawInt() throws IOException {
|
||||
// copied from java.io.Bits
|
||||
byte b0 = readByte();
|
||||
byte b1 = readByte();
|
||||
byte b2 = readByte();
|
||||
byte b3 = readByte();
|
||||
return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24);
|
||||
}
|
||||
|
||||
public final long readRawLong() throws IOException {
|
||||
// copied from java.io.Bits
|
||||
byte b0 = readByte();
|
||||
byte b1 = readByte();
|
||||
byte b2 = readByte();
|
||||
byte b3 = readByte();
|
||||
byte b4 = readByte();
|
||||
byte b5 = readByte();
|
||||
byte b6 = readByte();
|
||||
byte b7 = readByte();
|
||||
return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56);
|
||||
}
|
||||
|
||||
public final long position() throws IOException {
|
||||
return position;
|
||||
}
|
||||
|
||||
public final void position(long newPosition) throws IOException {
|
||||
if (!currentBlock.contains(newPosition)) {
|
||||
if (!previousBlock.contains(newPosition)) {
|
||||
if (newPosition > size()) {
|
||||
throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes.");
|
||||
}
|
||||
long blockStart = trimToFileSize(calculateBlockStart(newPosition));
|
||||
file.seek(blockStart);
|
||||
// trim amount to file size
|
||||
long amount = Math.min(size() - blockStart, blockSize);
|
||||
previousBlock.read(file, (int) amount);
|
||||
}
|
||||
// swap previous and current
|
||||
Block tmp = currentBlock;
|
||||
currentBlock = previousBlock;
|
||||
previousBlock = tmp;
|
||||
}
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
private final long trimToFileSize(long position) throws IOException {
|
||||
return Math.min(size(), Math.max(0, position));
|
||||
}
|
||||
|
||||
private final long calculateBlockStart(long newPosition) {
|
||||
// align to end of current block
|
||||
if (currentBlock.contains(newPosition - blockSize)) {
|
||||
return currentBlock.blockPosition + currentBlock.bytes.length;
|
||||
}
|
||||
// align before current block
|
||||
if (currentBlock.contains(newPosition + blockSize)) {
|
||||
return currentBlock.blockPosition - blockSize;
|
||||
}
|
||||
// not near current block, pick middle
|
||||
return newPosition - blockSize / 2;
|
||||
}
|
||||
|
||||
public final long size() throws IOException {
|
||||
return size;
|
||||
}
|
||||
|
||||
public final void close() throws IOException {
|
||||
file.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int skipBytes(int n) throws IOException {
|
||||
long position = position();
|
||||
position(position + n);
|
||||
return (int) (position() - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean readBoolean() throws IOException {
|
||||
return readByte() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte() throws IOException {
|
||||
return readByte() & 0x00FF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort() throws IOException {
|
||||
return readShort() & 0xFFFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String readLine() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// NOTE, this method should really be called readString
|
||||
// but can't be renamed without making RecordingInput a
|
||||
// public class.
|
||||
//
|
||||
// This method DOES Not read as expected (s2 + utf8 encoded character)
|
||||
// instead it read:
|
||||
// byte encoding
|
||||
// int size
|
||||
// data (byte or char)
|
||||
//
|
||||
// where encoding
|
||||
//
|
||||
// 0, means null
|
||||
// 1, means UTF8 encoded byte array
|
||||
// 2, means char array
|
||||
// 3, means latin-1 (ISO-8859-1) encoded byte array
|
||||
// 4, means ""
|
||||
@Override
|
||||
public String readUTF() throws IOException {
|
||||
return readEncodedString(readByte());
|
||||
}
|
||||
|
||||
public String readEncodedString(byte encoding) throws IOException {
|
||||
if (encoding == STRING_ENCODING_NULL) {
|
||||
return null;
|
||||
}
|
||||
if (encoding == STRING_ENCODING_EMPTY_STRING) {
|
||||
return "";
|
||||
}
|
||||
int size = readInt();
|
||||
require(size, "String size %d exceeds available data");
|
||||
if (encoding == STRING_ENCODING_CHAR_ARRAY) {
|
||||
char[] c = new char[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
c[i] = readChar();
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
byte[] bytes = new byte[size];
|
||||
readFully(bytes); // TODO: optimize, check size, and copy only if needed
|
||||
if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) {
|
||||
return new String(bytes, UTF8);
|
||||
}
|
||||
|
||||
if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) {
|
||||
return new String(bytes, LATIN1);
|
||||
}
|
||||
throw new IOException("Unknown string encoding " + encoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() throws IOException {
|
||||
return (char) readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
return (short) readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
return (int) readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
// can be optimized by branching checks, but will do for now
|
||||
byte b0 = readByte();
|
||||
long ret = (b0 & 0x7FL);
|
||||
if (b0 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b1 = readByte();
|
||||
ret += (b1 & 0x7FL) << 7;
|
||||
if (b1 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b2 = readByte();
|
||||
ret += (b2 & 0x7FL) << 14;
|
||||
if (b2 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b3 = readByte();
|
||||
ret += (b3 & 0x7FL) << 21;
|
||||
if (b3 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b4 = readByte();
|
||||
ret += (b4 & 0x7FL) << 28;
|
||||
if (b4 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b5 = readByte();
|
||||
ret += (b5 & 0x7FL) << 35;
|
||||
if (b5 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b6 = readByte();
|
||||
ret += (b6 & 0x7FL) << 42;
|
||||
if (b6 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b7 = readByte();
|
||||
ret += (b7 & 0x7FL) << 49;
|
||||
if (b7 >= 0) {
|
||||
return ret;
|
||||
}
|
||||
int b8 = readByte(); // read last byte raw
|
||||
return ret + (((long) (b8 & 0XFF)) << 56);
|
||||
}
|
||||
|
||||
// Purpose of this method is to prevent OOM by sanity check
|
||||
// the minimum required number of bytes against what is available in
|
||||
// segment/chunk/file
|
||||
public void require(int minimumBytes, String errorMessage) throws IOException {
|
||||
if (position + minimumBytes > size) {
|
||||
throw new IOException(String.format(errorMessage, minimumBytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 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;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedObject;
|
||||
import jdk.jfr.consumer.RecordingFile;
|
||||
import jdk.jfr.internal.Type;
|
||||
|
||||
public abstract class RecordingInternals {
|
||||
|
||||
public static RecordingInternals INSTANCE;
|
||||
|
||||
public abstract boolean isLastEventInChunk(RecordingFile file);
|
||||
|
||||
public abstract Object getOffsetDataTime(RecordedObject event, String name);
|
||||
|
||||
public abstract List<Type> readTypes(RecordingFile file) throws IOException;
|
||||
|
||||
public abstract void sort(List<RecordedEvent> events);
|
||||
|
||||
}
|
||||
201
jdkSrc/jdk8/jdk/jfr/internal/dcmd/AbstractDCmd.java
Normal file
201
jdkSrc/jdk8/jdk/jfr/internal/dcmd/AbstractDCmd.java
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.dcmd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
/**
|
||||
* Base class for JFR diagnostic commands
|
||||
*
|
||||
*/
|
||||
abstract class AbstractDCmd {
|
||||
|
||||
private final StringWriter result;
|
||||
private final PrintWriter log;
|
||||
|
||||
protected AbstractDCmd() {
|
||||
result = new StringWriter();
|
||||
log = new PrintWriter(result);
|
||||
}
|
||||
|
||||
protected final FlightRecorder getFlightRecorder() {
|
||||
return FlightRecorder.getFlightRecorder();
|
||||
}
|
||||
|
||||
public final String getResult() {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public String getPid() {
|
||||
// Invoking ProcessHandle.current().pid() would require loading more
|
||||
// classes during startup so instead JVM.getJVM().getPid() is used.
|
||||
// The pid will not be exposed to running Java application, only when starting
|
||||
// JFR from command line (-XX:StartFlightRecordin) or jcmd (JFR.start and JFR.check)
|
||||
return JVM.getJVM().getPid();
|
||||
}
|
||||
|
||||
protected final SafePath resolvePath(Recording recording, String filename) throws InvalidPathException {
|
||||
if (filename == null) {
|
||||
return makeGenerated(recording, Paths.get("."));
|
||||
}
|
||||
Path path = Paths.get(filename);
|
||||
if (Files.isDirectory(path)) {
|
||||
return makeGenerated(recording, path);
|
||||
}
|
||||
return new SafePath(path.toAbsolutePath().normalize());
|
||||
}
|
||||
|
||||
private SafePath makeGenerated(Recording recording, Path directory) {
|
||||
return new SafePath(directory.toAbsolutePath().resolve(Utils.makeFilename(recording)).normalize());
|
||||
}
|
||||
|
||||
protected final Recording findRecording(String name) throws DCmdException {
|
||||
try {
|
||||
return findRecordingById(Integer.parseInt(name));
|
||||
} catch (NumberFormatException nfe) {
|
||||
// User specified a name, not an id.
|
||||
return findRecordingByName(name);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void reportOperationComplete(String actionPrefix, String name, SafePath file) {
|
||||
print(actionPrefix);
|
||||
print(" recording");
|
||||
if (name != null) {
|
||||
print(" \"" + name + "\"");
|
||||
}
|
||||
if (file != null) {
|
||||
print(",");
|
||||
try {
|
||||
print(" ");
|
||||
long bytes = SecuritySupport.getFileSize(file);
|
||||
printBytes(bytes);
|
||||
} catch (IOException e) {
|
||||
// Ignore, not essential
|
||||
}
|
||||
println(" written to:");
|
||||
println();
|
||||
printPath(file);
|
||||
} else {
|
||||
println(".");
|
||||
}
|
||||
}
|
||||
|
||||
protected final List<Recording> getRecordings() {
|
||||
List<Recording> list = new ArrayList<>(getFlightRecorder().getRecordings());
|
||||
Collections.sort(list, Comparator.comparing(Recording::getId));
|
||||
return list;
|
||||
}
|
||||
|
||||
static String quoteIfNeeded(String text) {
|
||||
if (text.contains(" ")) {
|
||||
return "\\\"" + text + "\\\"";
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void println() {
|
||||
log.println();
|
||||
}
|
||||
|
||||
protected final void print(String s) {
|
||||
log.print(s);
|
||||
}
|
||||
|
||||
protected final void print(String s, Object... args) {
|
||||
log.printf(s, args);
|
||||
}
|
||||
|
||||
protected final void println(String s, Object... args) {
|
||||
print(s, args);
|
||||
println();
|
||||
}
|
||||
|
||||
protected final void printBytes(long bytes) {
|
||||
print(Utils.formatBytes(bytes));
|
||||
}
|
||||
|
||||
protected final void printTimespan(Duration timespan, String separator) {
|
||||
print(Utils.formatTimespan(timespan, separator));
|
||||
}
|
||||
|
||||
protected final void printPath(SafePath path) {
|
||||
if (path == null) {
|
||||
print("N/A");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
printPath(SecuritySupport.getAbsolutePath(path).toPath());
|
||||
} catch (IOException ioe) {
|
||||
printPath(path.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
protected final void printPath(Path path) {
|
||||
try {
|
||||
println(path.toAbsolutePath().toString());
|
||||
} catch (SecurityException e) {
|
||||
// fall back on filename
|
||||
println(path.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private Recording findRecordingById(int id) throws DCmdException {
|
||||
for (Recording r : getFlightRecorder().getRecordings()) {
|
||||
if (r.getId() == id) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
throw new DCmdException("Could not find %d.\n\nUse JFR.check without options to see list of all available recordings.", id);
|
||||
}
|
||||
|
||||
private Recording findRecordingByName(String name) throws DCmdException {
|
||||
for (Recording recording : getFlightRecorder().getRecordings()) {
|
||||
if (name.equals(recording.getName())) {
|
||||
return recording;
|
||||
}
|
||||
}
|
||||
throw new DCmdException("Could not find %s.\n\nUse JFR.check without options to see list of all available recordings.", name);
|
||||
}
|
||||
}
|
||||
164
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdCheck.java
Normal file
164
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdCheck.java
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.dcmd;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
/**
|
||||
* JFR.check - invoked from native
|
||||
*
|
||||
*/
|
||||
final class DCmdCheck extends AbstractDCmd {
|
||||
/**
|
||||
* Execute JFR.check
|
||||
*
|
||||
* @param recordingText name or id of the recording to check, or
|
||||
* <code>null</code> to show a list of all recordings.
|
||||
*
|
||||
* @param verbose if event settings should be included.
|
||||
*
|
||||
* @return result output
|
||||
*
|
||||
* @throws DCmdException if the check could not be completed.
|
||||
*/
|
||||
public String execute(String recordingText, Boolean verbose) throws DCmdException {
|
||||
executeInternal(recordingText, verbose);
|
||||
return getResult();
|
||||
}
|
||||
|
||||
private void executeInternal(String name, Boolean verbose) throws DCmdException {
|
||||
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdCheck: name=" + name + ", verbose=" + verbose);
|
||||
}
|
||||
|
||||
if (verbose == null) {
|
||||
verbose = Boolean.FALSE;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
printRecording(findRecording(name), verbose);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Recording> recordings = getRecordings();
|
||||
if (!verbose && recordings.isEmpty()) {
|
||||
println("No available recordings.");
|
||||
println();
|
||||
println("Use jcmd " + getPid() + " JFR.start to start a recording.");
|
||||
return;
|
||||
}
|
||||
boolean first = true;
|
||||
for (Recording recording : recordings) {
|
||||
// Print separation between recordings,
|
||||
if (!first) {
|
||||
println();
|
||||
if (Boolean.TRUE.equals(verbose)) {
|
||||
println();
|
||||
}
|
||||
}
|
||||
first = false;
|
||||
printRecording(recording, verbose);
|
||||
}
|
||||
}
|
||||
|
||||
private void printRecording(Recording recording, boolean verbose) {
|
||||
printGeneral(recording);
|
||||
if (verbose) {
|
||||
println();
|
||||
printSetttings(recording);
|
||||
}
|
||||
}
|
||||
|
||||
private void printGeneral(Recording recording) {
|
||||
print("Recording " + recording.getId() + ": name=" + recording.getName());
|
||||
|
||||
Duration duration = recording.getDuration();
|
||||
if (duration != null) {
|
||||
print(" duration=");
|
||||
printTimespan(duration, "");
|
||||
}
|
||||
|
||||
long maxSize = recording.getMaxSize();
|
||||
if (maxSize != 0) {
|
||||
print(" maxsize=");
|
||||
print(Utils.formatBytesCompact(maxSize));
|
||||
}
|
||||
Duration maxAge = recording.getMaxAge();
|
||||
if (maxAge != null) {
|
||||
print(" maxage=");
|
||||
printTimespan(maxAge, "");
|
||||
}
|
||||
|
||||
print(" (" + recording.getState().toString().toLowerCase() + ")");
|
||||
println();
|
||||
}
|
||||
|
||||
private void printSetttings(Recording recording) {
|
||||
Map<String, String> settings = recording.getSettings();
|
||||
for (EventType eventType : sortByEventPath(getFlightRecorder().getEventTypes())) {
|
||||
StringJoiner sj = new StringJoiner(",", "[", "]");
|
||||
sj.setEmptyValue("");
|
||||
for (SettingDescriptor s : eventType.getSettingDescriptors()) {
|
||||
String settingsPath = eventType.getName() + "#" + s.getName();
|
||||
if (settings.containsKey(settingsPath)) {
|
||||
sj.add(s.getName() + "=" + settings.get(settingsPath));
|
||||
}
|
||||
}
|
||||
String settingsText = sj.toString();
|
||||
if (!settingsText.isEmpty()) {
|
||||
print(" %s (%s)", eventType.getLabel(), eventType.getName());
|
||||
println();
|
||||
println(" " + settingsText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<EventType> sortByEventPath(Collection<EventType> events) {
|
||||
List<EventType> sorted = new ArrayList<>();
|
||||
sorted.addAll(events);
|
||||
Collections.sort(sorted, new Comparator<EventType>() {
|
||||
@Override
|
||||
public int compare(EventType e1, EventType e2) {
|
||||
return e1.getName().compareTo(e2.getName());
|
||||
}
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
}
|
||||
217
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdConfigure.java
Normal file
217
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdConfigure.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.dcmd;
|
||||
|
||||
|
||||
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.Options;
|
||||
import jdk.jfr.internal.Repository;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
/**
|
||||
* JFR.configure - invoked from native
|
||||
*
|
||||
*/
|
||||
//Instantiated by native
|
||||
final class DCmdConfigure extends AbstractDCmd {
|
||||
/**
|
||||
* Execute JFR.configure.
|
||||
*
|
||||
* @param repositoryPath the path
|
||||
* @param dumpPath path to dump to on fatal error (oom)
|
||||
* @param stackDepth depth of stack traces
|
||||
* @param globalBufferCount number of global buffers
|
||||
* @param globalBufferSize size of global buffers
|
||||
* @param threadBufferSize size of thread buffer for events
|
||||
* @param maxChunkSize threshold at which a new chunk is created in the disk repository
|
||||
* @param sampleThreads if thread sampling should be enabled
|
||||
*
|
||||
* @return result
|
||||
|
||||
* @throws DCmdException
|
||||
* if the dump could not be completed
|
||||
*/
|
||||
public String execute
|
||||
(
|
||||
String repositoryPath,
|
||||
String dumpPath,
|
||||
Integer stackDepth,
|
||||
Long globalBufferCount,
|
||||
Long globalBufferSize,
|
||||
Long threadBufferSize,
|
||||
Long memorySize,
|
||||
Long maxChunkSize,
|
||||
Boolean sampleThreads
|
||||
|
||||
) throws DCmdException {
|
||||
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdConfigure: repositorypath=" + repositoryPath +
|
||||
", dumppath=" + dumpPath +
|
||||
", stackdepth=" + stackDepth +
|
||||
", globalbuffercount=" + globalBufferCount +
|
||||
", globalbuffersize=" + globalBufferSize +
|
||||
", thread_buffer_size" + threadBufferSize +
|
||||
", memorysize" + memorySize +
|
||||
", maxchunksize=" + maxChunkSize +
|
||||
", samplethreads" + sampleThreads);
|
||||
}
|
||||
|
||||
|
||||
boolean updated = false;
|
||||
if (repositoryPath != null) {
|
||||
try {
|
||||
SafePath s = new SafePath(repositoryPath);
|
||||
Repository.getRepository().setBasePath(s);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Base repository path set to " + repositoryPath);
|
||||
} catch (Exception e) {
|
||||
throw new DCmdException("Could not use " + repositoryPath + " as repository. " + e.getMessage(), e);
|
||||
}
|
||||
printRepositoryPath();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (dumpPath != null) {
|
||||
Options.setDumpPath(new SafePath(dumpPath));
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Emergency dump path set to " + dumpPath);
|
||||
printDumpPath();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (stackDepth != null) {
|
||||
Options.setStackDepth(stackDepth);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Stack depth set to " + stackDepth);
|
||||
printStackDepth();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (globalBufferCount != null) {
|
||||
Options.setGlobalBufferCount(globalBufferCount);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Global buffer count set to " + globalBufferCount);
|
||||
printGlobalBufferCount();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (globalBufferSize != null) {
|
||||
Options.setGlobalBufferSize(globalBufferSize);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Global buffer size set to " + globalBufferSize);
|
||||
printGlobalBufferSize();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (threadBufferSize != null) {
|
||||
Options.setThreadBufferSize(threadBufferSize);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Thread buffer size set to " + threadBufferSize);
|
||||
printThreadBufferSize();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (memorySize != null) {
|
||||
Options.setMemorySize(memorySize);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Memory size set to " + memorySize);
|
||||
printMemorySize();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (maxChunkSize != null) {
|
||||
Options.setMaxChunkSize(maxChunkSize);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Max chunk size set to " + maxChunkSize);
|
||||
printMaxChunkSize();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (sampleThreads != null) {
|
||||
Options.setSampleThreads(sampleThreads);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Sample threads set to " + sampleThreads);
|
||||
printSampleThreads();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!updated) {
|
||||
println("Current configuration:");
|
||||
println();
|
||||
printRepositoryPath();
|
||||
printStackDepth();
|
||||
printGlobalBufferCount();
|
||||
printGlobalBufferSize();
|
||||
printThreadBufferSize();
|
||||
printMemorySize();
|
||||
printMaxChunkSize();
|
||||
printSampleThreads();
|
||||
}
|
||||
return getResult();
|
||||
}
|
||||
|
||||
private void printRepositoryPath() {
|
||||
print("Repository path: ");
|
||||
printPath(Repository.getRepository().getRepositoryPath());
|
||||
println();
|
||||
}
|
||||
|
||||
private void printDumpPath() {
|
||||
print("Dump path: ");
|
||||
printPath(Options.getDumpPath());
|
||||
println();
|
||||
}
|
||||
|
||||
private void printSampleThreads() {
|
||||
println("Sample threads: " + Options.getSampleThreads());
|
||||
}
|
||||
|
||||
private void printStackDepth() {
|
||||
println("Stack depth: " + Options.getStackDepth());
|
||||
}
|
||||
|
||||
private void printGlobalBufferCount() {
|
||||
println("Global buffer count: " + Options.getGlobalBufferCount());
|
||||
}
|
||||
|
||||
private void printGlobalBufferSize() {
|
||||
print("Global buffer size: ");
|
||||
printBytes(Options.getGlobalBufferSize());
|
||||
println();
|
||||
}
|
||||
|
||||
private void printThreadBufferSize() {
|
||||
print("Thread buffer size: ");
|
||||
printBytes(Options.getThreadBufferSize());
|
||||
println();
|
||||
}
|
||||
|
||||
private void printMemorySize() {
|
||||
print("Memory size: ");
|
||||
printBytes(Options.getMemorySize());
|
||||
println();
|
||||
}
|
||||
|
||||
private void printMaxChunkSize() {
|
||||
print("Max chunk size: ");
|
||||
printBytes(Options.getMaxChunkSize());
|
||||
println();
|
||||
}
|
||||
}
|
||||
218
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdDump.java
Normal file
218
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdDump.java
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.dcmd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.PlatformRecorder;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.Utils;
|
||||
import jdk.jfr.internal.WriteableUserPath;
|
||||
|
||||
/**
|
||||
* JFR.dump
|
||||
*
|
||||
*/
|
||||
// Instantiated by native
|
||||
final class DCmdDump extends AbstractDCmd {
|
||||
/**
|
||||
* Execute JFR.dump.
|
||||
*
|
||||
* @param name name or id of the recording to dump, or <code>null</code> to dump everything
|
||||
*
|
||||
* @param filename file path where recording should be written, not null
|
||||
* @param maxAge how far back in time to dump, may be null
|
||||
* @param maxSize how far back in size to dump data from, may be null
|
||||
* @param begin point in time to dump data from, may be null
|
||||
* @param end point in time to dump data to, may be null
|
||||
* @param pathToGcRoots if Java heap should be swept for reference chains
|
||||
*
|
||||
* @return result output
|
||||
*
|
||||
* @throws DCmdException if the dump could not be completed
|
||||
*/
|
||||
public String execute(String name, String filename, Long maxAge, Long maxSize, String begin, String end, Boolean pathToGcRoots) throws DCmdException {
|
||||
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG,
|
||||
"Executing DCmdDump: name=" + name +
|
||||
", filename=" + filename +
|
||||
", maxage=" + maxAge +
|
||||
", maxsize=" + maxSize +
|
||||
", begin=" + begin +
|
||||
", end" + end +
|
||||
", path-to-gc-roots=" + pathToGcRoots);
|
||||
}
|
||||
|
||||
if (FlightRecorder.getFlightRecorder().getRecordings().isEmpty()) {
|
||||
throw new DCmdException("No recordings to dump from. Use JFR.start to start a recording.");
|
||||
}
|
||||
|
||||
if (maxAge != null) {
|
||||
if (end != null || begin != null) {
|
||||
throw new DCmdException("Dump failed, maxage can't be combined with begin or end.");
|
||||
}
|
||||
|
||||
if (maxAge < 0) {
|
||||
throw new DCmdException("Dump failed, maxage can't be negative.");
|
||||
}
|
||||
if (maxAge == 0) {
|
||||
maxAge = Long.MAX_VALUE / 2; // a high value that won't overflow
|
||||
}
|
||||
}
|
||||
|
||||
if (maxSize!= null) {
|
||||
if (maxSize < 0) {
|
||||
throw new DCmdException("Dump failed, maxsize can't be negative.");
|
||||
}
|
||||
if (maxSize == 0) {
|
||||
maxSize = Long.MAX_VALUE / 2; // a high value that won't overflow
|
||||
}
|
||||
}
|
||||
|
||||
Instant beginTime = parseTime(begin, "begin");
|
||||
Instant endTime = parseTime(end, "end");
|
||||
|
||||
if (beginTime != null && endTime != null) {
|
||||
if (endTime.isBefore(beginTime)) {
|
||||
throw new DCmdException("Dump failed, begin must preceed end.");
|
||||
}
|
||||
}
|
||||
|
||||
Duration duration = null;
|
||||
if (maxAge != null) {
|
||||
duration = Duration.ofNanos(maxAge);
|
||||
beginTime = Instant.now().minus(duration);
|
||||
}
|
||||
Recording recording = null;
|
||||
if (name != null) {
|
||||
recording = findRecording(name);
|
||||
}
|
||||
PlatformRecorder recorder = PrivateAccess.getInstance().getPlatformRecorder();
|
||||
|
||||
try {
|
||||
synchronized (recorder) {
|
||||
dump(recorder, recording, name, filename, maxSize, pathToGcRoots, beginTime, endTime);
|
||||
}
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
throw new DCmdException("Dump failed. Could not copy recording data. %s", e.getMessage());
|
||||
}
|
||||
return getResult();
|
||||
}
|
||||
|
||||
public void dump(PlatformRecorder recorder, Recording recording, String name, String filename, Long maxSize, Boolean pathToGcRoots, Instant beginTime, Instant endTime) throws DCmdException, IOException {
|
||||
try (PlatformRecording r = newSnapShot(recorder, recording, pathToGcRoots)) {
|
||||
r.filter(beginTime, endTime, maxSize);
|
||||
if (r.getChunks().isEmpty()) {
|
||||
throw new DCmdException("Dump failed. No data found in the specified interval.");
|
||||
}
|
||||
// If a filename exist, use it
|
||||
// if a filename doesn't exist, use destination set earlier
|
||||
// if destination doesn't exist, generate a filename
|
||||
WriteableUserPath wup = null;
|
||||
if (recording != null) {
|
||||
PlatformRecording pRecording = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
wup = pRecording.getDestination();
|
||||
}
|
||||
if (filename != null || (filename == null && wup == null) ) {
|
||||
SafePath safe = resolvePath(recording, filename);
|
||||
wup = new WriteableUserPath(safe.toPath());
|
||||
}
|
||||
r.dumpStopped(wup);
|
||||
reportOperationComplete("Dumped", name, new SafePath(wup.getRealPathText()));
|
||||
}
|
||||
}
|
||||
|
||||
private Instant parseTime(String time, String parameter) throws DCmdException {
|
||||
if (time == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Instant.parse(time);
|
||||
} catch (DateTimeParseException dtp) {
|
||||
// fall through
|
||||
}
|
||||
try {
|
||||
LocalDateTime ldt = LocalDateTime.parse(time);
|
||||
return ZonedDateTime.of(ldt, ZoneId.systemDefault()).toInstant();
|
||||
} catch (DateTimeParseException dtp) {
|
||||
// fall through
|
||||
}
|
||||
try {
|
||||
LocalTime lt = LocalTime.parse(time);
|
||||
LocalDate ld = LocalDate.now();
|
||||
Instant instant = ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant();
|
||||
Instant now = Instant.now();
|
||||
if (instant.isAfter(now) && !instant.isBefore(now.plusSeconds(3600))) {
|
||||
// User must have meant previous day
|
||||
ld = ld.minusDays(1);
|
||||
}
|
||||
return ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant();
|
||||
} catch (DateTimeParseException dtp) {
|
||||
// fall through
|
||||
}
|
||||
|
||||
if (time.startsWith("-")) {
|
||||
try {
|
||||
long durationNanos = Utils.parseTimespan(time.substring(1));
|
||||
Duration duration = Duration.ofNanos(durationNanos);
|
||||
return Instant.now().minus(duration);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
throw new DCmdException("Dump failed, not a valid %s time.", parameter);
|
||||
}
|
||||
|
||||
private PlatformRecording newSnapShot(PlatformRecorder recorder, Recording recording, Boolean pathToGcRoots) throws DCmdException, IOException {
|
||||
if (recording == null) {
|
||||
// Operate on all recordings
|
||||
PlatformRecording snapshot = recorder.newTemporaryRecording();
|
||||
recorder.fillWithRecordedData(snapshot, pathToGcRoots);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
return pr.newSnapshotClone("Dumped by user", pathToGcRoots);
|
||||
}
|
||||
|
||||
}
|
||||
69
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdException.java
Normal file
69
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdException.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.dcmd;
|
||||
|
||||
import java.util.Formatter;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that a diagnostic command could not be executed
|
||||
* successfully.
|
||||
*/
|
||||
final class DCmdException extends Exception {
|
||||
private static final long serialVersionUID = -3792411099340016465L;
|
||||
|
||||
/**
|
||||
* Constructs a new exception with message derived from a format string.
|
||||
*
|
||||
* @param format format string as described in {@link Formatter} class.
|
||||
*
|
||||
* @param args arguments referenced by the format specifiers in the format
|
||||
* string.
|
||||
*
|
||||
*/
|
||||
public DCmdException(String format, Object... args) {
|
||||
super(format(format, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with message derived from a format string.
|
||||
*
|
||||
* @param cause exception that stopped the diagnostic command to complete.
|
||||
*
|
||||
* @param format format string as described in {@link Formatter} class.
|
||||
*
|
||||
* @param args arguments referenced by the format specifiers in the format
|
||||
* string.
|
||||
*
|
||||
*/
|
||||
public DCmdException(Throwable cause, String format, Object... args) {
|
||||
super(format(format, args), cause);
|
||||
}
|
||||
|
||||
private static String format(String message, Object... args) {
|
||||
try (Formatter formatter = new Formatter()) {
|
||||
return formatter.format(message, args).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
259
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdStart.java
Normal file
259
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdStart.java
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.dcmd;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.OldObjectSample;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.jfc.JFC;
|
||||
|
||||
/**
|
||||
* JFR.start
|
||||
*
|
||||
*/
|
||||
//Instantiated by native
|
||||
final class DCmdStart extends AbstractDCmd {
|
||||
|
||||
/**
|
||||
* Execute JFR.start.
|
||||
*
|
||||
* @param name optional name that can be used to identify recording.
|
||||
* @param settings names of settings files to use, i.e. "default" or
|
||||
* "default.jfc".
|
||||
* @param delay delay before recording is started, in nanoseconds. Must be
|
||||
* at least 1 second.
|
||||
* @param duration duration of the recording, in nanoseconds. Must be at
|
||||
* least 1 second.
|
||||
* @param disk if recording should be persisted to disk
|
||||
* @param path file path where recording data should be written
|
||||
* @param maxAge how long recording data should be kept in the disk
|
||||
* repository, or <code>0</code> if no limit should be set.
|
||||
*
|
||||
* @param maxSize the minimum amount data to keep in the disk repository
|
||||
* before it is discarded, or <code>0</code> if no limit should be
|
||||
* set.
|
||||
*
|
||||
* @param dumpOnExit if recording should dump on exit
|
||||
*
|
||||
* @return result output
|
||||
*
|
||||
* @throws DCmdException if recording could not be started
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
|
||||
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name +
|
||||
", settings=" + (settings != null ? Arrays.asList(settings) : "(none)") +
|
||||
", delay=" + delay +
|
||||
", duration=" + duration +
|
||||
", disk=" + disk+
|
||||
", filename=" + path +
|
||||
", maxage=" + maxAge +
|
||||
", maxsize=" + maxSize +
|
||||
", dumponexit =" + dumpOnExit +
|
||||
", path-to-gc-roots=" + pathToGcRoots);
|
||||
}
|
||||
if (name != null) {
|
||||
try {
|
||||
Integer.parseInt(name);
|
||||
throw new DCmdException("Name of recording can't be numeric");
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ok, can't be mixed up with name
|
||||
}
|
||||
}
|
||||
|
||||
if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) {
|
||||
throw new DCmdException("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename.");
|
||||
}
|
||||
if (settings.length == 1 && settings[0].length() == 0) {
|
||||
throw new DCmdException("No settings specified. Use settings=none to start without any settings");
|
||||
}
|
||||
Map<String, String> s = new HashMap<>();
|
||||
for (String configName : settings) {
|
||||
try {
|
||||
s.putAll(JFC.createKnown(configName).getSettings());
|
||||
} catch(FileNotFoundException e) {
|
||||
throw new DCmdException("Could not find settings file'" + configName + "'", e);
|
||||
} catch (IOException | ParseException e) {
|
||||
throw new DCmdException("Could not parse settings file '" + settings[0] + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
OldObjectSample.updateSettingPathToGcRoots(s, pathToGcRoots);
|
||||
|
||||
if (duration != null) {
|
||||
if (duration < 1000L * 1000L * 1000L) {
|
||||
// to avoid typo, duration below 1s makes no sense
|
||||
throw new DCmdException("Could not start recording, duration must be at least 1 second.");
|
||||
}
|
||||
}
|
||||
|
||||
if (delay != null) {
|
||||
if (delay < 1000L * 1000L * 1000) {
|
||||
// to avoid typo, delay shorter than 1s makes no sense.
|
||||
throw new DCmdException("Could not start recording, delay must be at least 1 second.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!FlightRecorder.isInitialized() && delay == null) {
|
||||
initializeWithForcedInstrumentation(s);
|
||||
}
|
||||
|
||||
Recording recording = new Recording();
|
||||
if (name != null) {
|
||||
recording.setName(name);
|
||||
}
|
||||
|
||||
if (disk != null) {
|
||||
recording.setToDisk(disk.booleanValue());
|
||||
}
|
||||
recording.setSettings(s);
|
||||
SafePath safePath = null;
|
||||
|
||||
if (path != null) {
|
||||
try {
|
||||
if (dumpOnExit == null) {
|
||||
// default to dumponexit=true if user specified filename
|
||||
dumpOnExit = Boolean.TRUE;
|
||||
}
|
||||
Path p = Paths.get(path);
|
||||
if (Files.isDirectory(p) && Boolean.TRUE.equals(dumpOnExit)) {
|
||||
// Decide destination filename at dump time
|
||||
// Purposely avoid generating filename in Recording#setDestination due to
|
||||
// security concerns
|
||||
PrivateAccess.getInstance().getPlatformRecording(recording).setDumpOnExitDirectory(new SafePath(p));
|
||||
} else {
|
||||
safePath = resolvePath(recording, path);
|
||||
recording.setDestination(safePath.toPath());
|
||||
}
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
recording.close();
|
||||
throw new DCmdException("Could not start recording, not able to write to file %s. %s ", path, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (maxAge != null) {
|
||||
recording.setMaxAge(Duration.ofNanos(maxAge));
|
||||
}
|
||||
|
||||
if (maxSize != null) {
|
||||
recording.setMaxSize(maxSize);
|
||||
}
|
||||
|
||||
if (duration != null) {
|
||||
recording.setDuration(Duration.ofNanos(duration));
|
||||
}
|
||||
|
||||
if (dumpOnExit != null) {
|
||||
recording.setDumpOnExit(dumpOnExit);
|
||||
}
|
||||
|
||||
if (delay != null) {
|
||||
Duration dDelay = Duration.ofNanos(delay);
|
||||
recording.scheduleStart(dDelay);
|
||||
print("Recording " + recording.getId() + " scheduled to start in ");
|
||||
printTimespan(dDelay, " ");
|
||||
print(".");
|
||||
} else {
|
||||
recording.start();
|
||||
print("Started recording " + recording.getId() + ".");
|
||||
}
|
||||
|
||||
if (recording.isToDisk() && duration == null && maxAge == null && maxSize == null) {
|
||||
print(" No limit specified, using maxsize=250MB as default.");
|
||||
recording.setMaxSize(250*1024L*1024L);
|
||||
}
|
||||
|
||||
if (safePath != null && duration != null) {
|
||||
println(" The result will be written to:");
|
||||
println();
|
||||
printPath(safePath);
|
||||
} else {
|
||||
println();
|
||||
println();
|
||||
String cmd = duration == null ? "dump" : "stop";
|
||||
String fileOption = path == null ? "filename=FILEPATH " : "";
|
||||
String recordingspecifier = "name=" + recording.getId();
|
||||
// if user supplied a name, use it.
|
||||
if (name != null) {
|
||||
recordingspecifier = "name=" + quoteIfNeeded(name);
|
||||
}
|
||||
print("Use jcmd " + getPid() + " JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file.");
|
||||
println();
|
||||
}
|
||||
return getResult();
|
||||
}
|
||||
|
||||
|
||||
// Instruments JDK-events on class load to reduce startup time
|
||||
private void initializeWithForcedInstrumentation(Map<String, String> settings) {
|
||||
if (!hasJDKEvents(settings)) {
|
||||
return;
|
||||
}
|
||||
JVM jvm = JVM.getJVM();
|
||||
try {
|
||||
jvm.setForceInstrumentation(true);
|
||||
FlightRecorder.getFlightRecorder();
|
||||
} finally {
|
||||
jvm.setForceInstrumentation(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasJDKEvents(Map<String, String> settings) {
|
||||
String[] eventNames = new String[7];
|
||||
eventNames[0] = "FileRead";
|
||||
eventNames[1] = "FileWrite";
|
||||
eventNames[2] = "SocketRead";
|
||||
eventNames[3] = "SocketWrite";
|
||||
eventNames[4] = "JavaErrorThrow";
|
||||
eventNames[5] = "JavaExceptionThrow";
|
||||
eventNames[6] = "FileForce";
|
||||
for (String eventName : eventNames) {
|
||||
if ("true".equals(settings.get(Type.EVENT_NAME_PREFIX + eventName + "#enabled"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
87
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdStop.java
Normal file
87
jdkSrc/jdk8/jdk/jfr/internal/dcmd/DCmdStop.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.dcmd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
/**
|
||||
* JFR.stop
|
||||
*
|
||||
*/
|
||||
// Instantiated by native
|
||||
final class DCmdStop extends AbstractDCmd {
|
||||
|
||||
/**
|
||||
* Execute JFR.stop
|
||||
*
|
||||
* Requires that either <code>name or <code>id</code> is set.
|
||||
*
|
||||
* @param name name or id of the recording to stop.
|
||||
*
|
||||
* @param filename file path where data should be written after recording has
|
||||
* been stopped, or <code>null</code> if recording shouldn't be written
|
||||
* to disk.
|
||||
* @return result text
|
||||
*
|
||||
* @throws DCmdException if recording could not be stopped
|
||||
*/
|
||||
public String execute(String name, String filename) throws DCmdException {
|
||||
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + ", filename=" + filename);
|
||||
}
|
||||
|
||||
try {
|
||||
SafePath safePath = null;
|
||||
Recording recording = findRecording(name);
|
||||
if (filename != null) {
|
||||
try {
|
||||
// Ensure path is valid. Don't generate safePath if filename == null, as a user may
|
||||
// want to stop recording without a dump
|
||||
safePath = resolvePath(null, filename);
|
||||
recording.setDestination(Paths.get(filename));
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
throw new DCmdException("Failed to stop %s. Could not set destination for \"%s\" to file %s", recording.getName(), filename, e.getMessage());
|
||||
}
|
||||
}
|
||||
recording.stop();
|
||||
reportOperationComplete("Stopped", recording.getName(), safePath);
|
||||
recording.close();
|
||||
return getResult();
|
||||
} catch (InvalidPathException | DCmdException e) {
|
||||
if (filename != null) {
|
||||
throw new DCmdException("Could not write recording \"%s\" to file. %s", name, e.getMessage());
|
||||
}
|
||||
throw new DCmdException(e, "Could not stop recording \"%s\".", name, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
123
jdkSrc/jdk8/jdk/jfr/internal/handlers/EventHandler.java
Normal file
123
jdkSrc/jdk8/jdk/jfr/internal/handlers/EventHandler.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.internal.EventControl;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.StringPool;
|
||||
|
||||
// Users should not be subclass for security reasons.
|
||||
public abstract class EventHandler {
|
||||
// Accessed by generated sub class
|
||||
protected final PlatformEventType platformEventType;
|
||||
|
||||
private final EventType eventType;
|
||||
private final EventControl eventControl;
|
||||
|
||||
// Accessed by generated sub class
|
||||
protected EventHandler(boolean registered, EventType eventType, EventControl eventControl) {
|
||||
if (System.getSecurityManager() != null) {
|
||||
// Do not allow user subclasses when security is enforced.
|
||||
if (EventHandler.class.getClassLoader() != this.getClass().getClassLoader()) {
|
||||
throw new SecurityException("Illegal subclass");
|
||||
}
|
||||
}
|
||||
this.eventType = eventType;
|
||||
this.platformEventType = PrivateAccess.getInstance().getPlatformEventType(eventType);
|
||||
this.eventControl = eventControl;
|
||||
platformEventType.setRegistered(registered);
|
||||
}
|
||||
|
||||
final protected StringPool createStringFieldWriter() {
|
||||
return new StringPool();
|
||||
}
|
||||
|
||||
// Accessed by generated code in event class
|
||||
public final boolean shouldCommit(long duration) {
|
||||
return isEnabled() && duration >= platformEventType.getThresholdTicks();
|
||||
}
|
||||
|
||||
// Accessed by generated code in event class
|
||||
// Accessed by generated sub class
|
||||
public final boolean isEnabled() {
|
||||
return platformEventType.isCommitable();
|
||||
}
|
||||
|
||||
public final EventType getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public final PlatformEventType getPlatformEventType() {
|
||||
return platformEventType;
|
||||
}
|
||||
|
||||
public final EventControl getEventControl() {
|
||||
return eventControl;
|
||||
}
|
||||
|
||||
public static long timestamp() {
|
||||
return JVM.counterTime();
|
||||
}
|
||||
|
||||
public static long duration(long startTime) {
|
||||
if (startTime == 0) {
|
||||
// User forgot to invoke begin, or instrumentation was
|
||||
// added after the user invoked begin.
|
||||
// Returning 0 will make it an instant event
|
||||
return 0;
|
||||
}
|
||||
return timestamp() - startTime;
|
||||
}
|
||||
|
||||
// Prevent a malicious user from instantiating a generated event handlers.
|
||||
@Override
|
||||
public final Object clone() throws java.lang.CloneNotSupportedException {
|
||||
throw new CloneNotSupportedException();
|
||||
}
|
||||
|
||||
private final void writeObject(ObjectOutputStream out) throws IOException {
|
||||
throw new IOException("Object cannot be serialized");
|
||||
}
|
||||
|
||||
private final void readObject(ObjectInputStream in) throws IOException {
|
||||
throw new IOException("Class cannot be deserialized");
|
||||
}
|
||||
|
||||
public boolean isRegistered() {
|
||||
return platformEventType.isRegistered();
|
||||
}
|
||||
|
||||
public boolean setRegistered(boolean registered) {
|
||||
return platformEventType.setRegistered(registered);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.instrument;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
final class ConstructorTracerWriter extends ClassVisitor {
|
||||
|
||||
private ConstructorWriter useInputParameter, noUseInputParameter;
|
||||
|
||||
static byte[] generateBytes(Class<?> clz, byte[] oldBytes) throws IOException {
|
||||
InputStream in = new ByteArrayInputStream(oldBytes);
|
||||
ClassReader cr = new ClassReader(in);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
ConstructorTracerWriter ctw = new ConstructorTracerWriter(cw, clz);
|
||||
cr.accept(ctw, 0);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private ConstructorTracerWriter(ClassVisitor cv, Class<?> classToChange) {
|
||||
super(Opcodes.ASM5, cv);
|
||||
useInputParameter = new ConstructorWriter(classToChange, true);
|
||||
noUseInputParameter = new ConstructorWriter(classToChange, false);
|
||||
}
|
||||
|
||||
private boolean isConstructor(String name) {
|
||||
return name.equals("<init>");
|
||||
}
|
||||
|
||||
private boolean takesStringParameter(String desc) {
|
||||
Type[] types = Type.getArgumentTypes(desc);
|
||||
if (types.length > 0 && types[0].getClassName().equals(String.class.getName())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
|
||||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
|
||||
// Get a hold of the constructors that takes a String as a parameter
|
||||
if (isConstructor(name)) {
|
||||
if (takesStringParameter(desc)) {
|
||||
useInputParameter.setMethodVisitor(mv);
|
||||
return useInputParameter;
|
||||
}
|
||||
noUseInputParameter.setMethodVisitor(mv);
|
||||
return noUseInputParameter;
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.instrument;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
final class ConstructorWriter extends MethodVisitor {
|
||||
|
||||
private boolean useInputParameter;
|
||||
private String shortClassName;
|
||||
private String fullClassName;
|
||||
|
||||
ConstructorWriter(Class<?> classToChange, boolean useInputParameter) {
|
||||
super(Opcodes.ASM5);
|
||||
this.useInputParameter = useInputParameter;
|
||||
shortClassName = classToChange.getSimpleName();
|
||||
fullClassName = classToChange.getName().replace('.', '/');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode)
|
||||
{
|
||||
if (opcode == RETURN) {
|
||||
if (useInputParameter) {
|
||||
useInput();
|
||||
} else {
|
||||
noInput();
|
||||
}
|
||||
}
|
||||
mv.visitInsn(opcode);
|
||||
}
|
||||
@SuppressWarnings("deprecation")
|
||||
private void useInput()
|
||||
{
|
||||
//Load 'this' from local variable 0
|
||||
//Load first input parameter
|
||||
//Invoke ThrowableTracer.traceCLASS(this, parameter) for current class
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "jdk/jfr/internal/instrument/ThrowableTracer",
|
||||
"trace" + shortClassName, "(L" + fullClassName +
|
||||
";Ljava/lang/String;)V");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void noInput()
|
||||
{
|
||||
//Load 'this' from local variable 0
|
||||
//Load ""
|
||||
//Invoke ThrowableTracer.traceCLASS(this, "") for current class
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "jdk/jfr/internal/instrument/ThrowableTracer",
|
||||
"trace" + shortClassName, "(L" + fullClassName +
|
||||
";Ljava/lang/String;)V");
|
||||
}
|
||||
|
||||
public void setMethodVisitor(MethodVisitor mv) {
|
||||
this.mv = mv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.jfr.events.FileForceEvent;
|
||||
import jdk.jfr.events.FileReadEvent;
|
||||
import jdk.jfr.events.FileWriteEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("sun.nio.ch.FileChannelImpl")
|
||||
final class FileChannelImplInstrumentor {
|
||||
|
||||
private FileChannelImplInstrumentor() {
|
||||
}
|
||||
|
||||
private String path;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void force(boolean metaData) throws IOException {
|
||||
FileForceEvent event = FileForceEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
force(metaData);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
force(metaData);
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.metaData = metaData;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(dst);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(dst);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(ByteBuffer dst, long position) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(dst, position);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(dst, position);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(dsts, offset, length);
|
||||
}
|
||||
long bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(dsts, offset, length);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return write(src);
|
||||
}
|
||||
int bytesWritten = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesWritten = write(src);
|
||||
} finally {
|
||||
event.bytesWritten = bytesWritten > 0 ? bytesWritten : 0;
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int write(ByteBuffer src, long position) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return write(src, position);
|
||||
}
|
||||
|
||||
int bytesWritten = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesWritten = write(src, position);
|
||||
} finally {
|
||||
event.bytesWritten = bytesWritten > 0 ? bytesWritten : 0;
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return write(srcs, offset, length);
|
||||
}
|
||||
long bytesWritten = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesWritten = write(srcs, offset, length);
|
||||
} finally {
|
||||
event.bytesWritten = bytesWritten > 0 ? bytesWritten : 0;
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jdk.jfr.events.FileReadEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("java.io.FileInputStream")
|
||||
final class FileInputStreamInstrumentor {
|
||||
|
||||
private FileInputStreamInstrumentor() {
|
||||
}
|
||||
|
||||
private String path;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read() throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read();
|
||||
}
|
||||
int result = 0;
|
||||
try {
|
||||
event.begin();
|
||||
result = read();
|
||||
if (result < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = 1;
|
||||
}
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(byte b[]) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(b);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(b);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(b, off, len);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(b, off, len);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jdk.jfr.events.FileWriteEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("java.io.FileOutputStream")
|
||||
final class FileOutputStreamInstrumentor {
|
||||
|
||||
private FileOutputStreamInstrumentor() {
|
||||
}
|
||||
|
||||
private String path;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void write(int b) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
write(b);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
write(b);
|
||||
event.bytesWritten = 1;
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void write(byte b[]) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
write(b);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
write(b);
|
||||
event.bytesWritten = b.length;
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
write(b, off, len);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
write(b, off, len);
|
||||
event.bytesWritten = len;
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
144
jdkSrc/jdk8/jdk/jfr/internal/instrument/JDKEvents.java
Normal file
144
jdkSrc/jdk8/jdk/jfr/internal/instrument/JDKEvents.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.instrument;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.events.ActiveRecordingEvent;
|
||||
import jdk.jfr.events.ActiveSettingEvent;
|
||||
import jdk.jfr.events.ErrorThrownEvent;
|
||||
import jdk.jfr.events.ExceptionStatisticsEvent;
|
||||
import jdk.jfr.events.ExceptionThrownEvent;
|
||||
import jdk.jfr.events.FileForceEvent;
|
||||
import jdk.jfr.events.FileReadEvent;
|
||||
import jdk.jfr.events.FileWriteEvent;
|
||||
import jdk.jfr.events.SocketReadEvent;
|
||||
import jdk.jfr.events.SocketWriteEvent;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.RequestEngine;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
|
||||
public final class JDKEvents {
|
||||
|
||||
private static final Class<?>[] eventClasses = {
|
||||
FileForceEvent.class,
|
||||
FileReadEvent.class,
|
||||
FileWriteEvent.class,
|
||||
SocketReadEvent.class,
|
||||
SocketWriteEvent.class,
|
||||
ExceptionThrownEvent.class,
|
||||
ExceptionStatisticsEvent.class,
|
||||
ErrorThrownEvent.class,
|
||||
ActiveSettingEvent.class,
|
||||
ActiveRecordingEvent.class
|
||||
};
|
||||
|
||||
// This is a list of the classes with instrumentation code that should be applied.
|
||||
private static final Class<?>[] instrumentationClasses = new Class<?>[] {
|
||||
FileInputStreamInstrumentor.class,
|
||||
FileOutputStreamInstrumentor.class,
|
||||
RandomAccessFileInstrumentor.class,
|
||||
FileChannelImplInstrumentor.class,
|
||||
SocketInputStreamInstrumentor.class,
|
||||
SocketOutputStreamInstrumentor.class,
|
||||
SocketChannelImplInstrumentor.class
|
||||
};
|
||||
|
||||
private static final Class<?>[] targetClasses = new Class<?>[instrumentationClasses.length];
|
||||
private static final JVM jvm = JVM.getJVM();
|
||||
private static final Runnable emitExceptionStatistics = JDKEvents::emitExceptionStatistics;
|
||||
private static boolean initializationTriggered;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized static void initialize() {
|
||||
try {
|
||||
if (initializationTriggered == false) {
|
||||
for (Class<?> eventClass : eventClasses) {
|
||||
SecuritySupport.registerEvent((Class<? extends Event>) eventClass);
|
||||
}
|
||||
initializationTriggered = true;
|
||||
RequestEngine.addTrustedJDKHook(ExceptionStatisticsEvent.class, emitExceptionStatistics);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not initialize JDK events. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void addInstrumentation() {
|
||||
try {
|
||||
List<Class<?>> list = new ArrayList<>();
|
||||
for (int i = 0; i < instrumentationClasses.length; i++) {
|
||||
JIInstrumentationTarget tgt = instrumentationClasses[i].getAnnotation(JIInstrumentationTarget.class);
|
||||
Class<?> clazz = Class.forName(tgt.value());
|
||||
targetClasses[i] = clazz;
|
||||
list.add(clazz);
|
||||
}
|
||||
list.add(java.lang.Throwable.class);
|
||||
list.add(java.lang.Error.class);
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Retransformed JDK classes");
|
||||
jvm.retransformClasses(list.toArray(new Class<?>[list.size()]));
|
||||
} catch (Exception e) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not add instrumentation for JDK events. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitExceptionStatistics() {
|
||||
ExceptionStatisticsEvent t = new ExceptionStatisticsEvent();
|
||||
t.throwables = ThrowableTracer.numThrowables();
|
||||
t.commit();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static byte[] retransformCallback(Class<?> klass, byte[] oldBytes) throws Throwable {
|
||||
if (java.lang.Throwable.class == klass) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, "Instrumenting java.lang.Throwable");
|
||||
return ConstructorTracerWriter.generateBytes(java.lang.Throwable.class, oldBytes);
|
||||
}
|
||||
|
||||
if (java.lang.Error.class == klass) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, "Instrumenting java.lang.Error");
|
||||
return ConstructorTracerWriter.generateBytes(java.lang.Error.class, oldBytes);
|
||||
}
|
||||
|
||||
for (int i = 0; i < targetClasses.length; i++) {
|
||||
if (targetClasses[i].equals(klass)) {
|
||||
Class<?> c = instrumentationClasses[i];
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, () -> "Processing instrumentation class: " + c);
|
||||
return new JIClassInstrumentation(instrumentationClasses[i], klass, oldBytes).getNewBytes();
|
||||
}
|
||||
}
|
||||
return oldBytes;
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
RequestEngine.removeHook(JDKEvents::emitExceptionStatistics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.tree.ClassNode;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
/**
|
||||
* This class will perform byte code instrumentation given an "instrumentor" class.
|
||||
*
|
||||
* @see JITracer
|
||||
*
|
||||
* @author Staffan Larsen
|
||||
*/
|
||||
@Deprecated
|
||||
final class JIClassInstrumentation {
|
||||
private final Class<?> instrumentor;
|
||||
private final String targetName;
|
||||
private final String instrumentorName;
|
||||
private final byte[] newBytes;
|
||||
private final ClassReader targetClassReader;
|
||||
private final ClassReader instrClassReader;
|
||||
|
||||
/**
|
||||
* Creates an instance and performs the instrumentation.
|
||||
*
|
||||
* @param instrumentor instrumentor class
|
||||
* @param target target class
|
||||
* @param old_target_bytes bytes in target
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
JIClassInstrumentation(Class<?> instrumentor, Class<?> target, byte[] old_target_bytes) throws ClassNotFoundException, IOException {
|
||||
instrumentorName = instrumentor.getName();
|
||||
this.targetName = target.getName();
|
||||
this.instrumentor = instrumentor;
|
||||
this.targetClassReader = new ClassReader(old_target_bytes);
|
||||
this.instrClassReader = new ClassReader(getOriginalClassBytes(instrumentor));
|
||||
this.newBytes = makeBytecode();
|
||||
Utils.writeGeneratedASM(target.getName(), newBytes);
|
||||
}
|
||||
|
||||
private static byte[] getOriginalClassBytes(Class<?> clazz) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
String name = "/" + clazz.getName().replace(".", "/") + ".class";
|
||||
InputStream is = SecuritySupport.getResourceAsStream(name);
|
||||
int bytesRead;
|
||||
byte[] buffer = new byte[16384];
|
||||
while ((bytesRead = is.read(buffer, 0, buffer.length)) != -1) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
baos.flush();
|
||||
is.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] makeBytecode() throws IOException, ClassNotFoundException {
|
||||
|
||||
// Find the methods to instrument and inline
|
||||
|
||||
final List<Method> instrumentationMethods = new ArrayList<>();
|
||||
for (final Method m : instrumentor.getDeclaredMethods()) {
|
||||
JIInstrumentationMethod im = m.getAnnotation(JIInstrumentationMethod.class);
|
||||
if (im != null) {
|
||||
instrumentationMethods.add(m);
|
||||
}
|
||||
}
|
||||
|
||||
// We begin by inlining the target's methods into the instrumentor
|
||||
|
||||
ClassNode temporary = new ClassNode();
|
||||
ClassVisitor inliner = new JIInliner(
|
||||
Opcodes.ASM5,
|
||||
temporary,
|
||||
targetName,
|
||||
instrumentorName,
|
||||
targetClassReader,
|
||||
instrumentationMethods);
|
||||
instrClassReader.accept(inliner, ClassReader.EXPAND_FRAMES);
|
||||
|
||||
// Now we have the target's methods inlined into the instrumentation code (in 'temporary').
|
||||
// We now need to replace the target's method with the code in the
|
||||
// instrumentation method.
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
JIMethodMergeAdapter ma = new JIMethodMergeAdapter(
|
||||
cw,
|
||||
temporary,
|
||||
instrumentationMethods,
|
||||
instrumentor.getAnnotationsByType(JITypeMapping.class));
|
||||
targetClassReader.accept(ma, ClassReader.EXPAND_FRAMES);
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instrumented byte codes that can be used to retransform the class.
|
||||
*
|
||||
* @return bytes
|
||||
*/
|
||||
public byte[] getNewBytes() {
|
||||
return newBytes.clone();
|
||||
}
|
||||
}
|
||||
113
jdkSrc/jdk8/jdk/jfr/internal/instrument/JIInliner.java
Normal file
113
jdkSrc/jdk8/jdk/jfr/internal/instrument/JIInliner.java
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.tree.ClassNode;
|
||||
import jdk.internal.org.objectweb.asm.tree.MethodNode;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
|
||||
@Deprecated
|
||||
final class JIInliner extends ClassVisitor {
|
||||
private final String targetClassName;
|
||||
private final String instrumentationClassName;
|
||||
private final ClassNode targetClassNode;
|
||||
private final List<Method> instrumentationMethods;
|
||||
|
||||
/**
|
||||
* A ClassVisitor which will check all methods of the class it visits against the instrumentationMethods
|
||||
* list. If a method is on that list, the method will be further processed for inlining into that
|
||||
* method.
|
||||
*/
|
||||
JIInliner(int api, ClassVisitor cv, String targetClassName, String instrumentationClassName,
|
||||
ClassReader targetClassReader,
|
||||
List<Method> instrumentationMethods) {
|
||||
super(api, cv);
|
||||
this.targetClassName = targetClassName;
|
||||
this.instrumentationClassName = instrumentationClassName;
|
||||
this.instrumentationMethods = instrumentationMethods;
|
||||
|
||||
ClassNode cn = new ClassNode(Opcodes.ASM5);
|
||||
targetClassReader.accept(cn, ClassReader.EXPAND_FRAMES);
|
||||
this.targetClassNode = cn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
|
||||
if (isInstrumentationMethod(name, desc)) {
|
||||
MethodNode methodToInline = findTargetMethodNode(name, desc);
|
||||
if (methodToInline == null) {
|
||||
throw new IllegalArgumentException("Could not find the method to instrument in the target class");
|
||||
}
|
||||
if (Modifier.isNative(methodToInline.access)) {
|
||||
throw new IllegalArgumentException("Cannot instrument native methods: " + targetClassNode.name + "." + methodToInline.name + methodToInline.desc);
|
||||
}
|
||||
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Inliner processing method " + name + desc);
|
||||
|
||||
JIMethodCallInliner mci = new JIMethodCallInliner(access,
|
||||
desc,
|
||||
mv,
|
||||
methodToInline,
|
||||
targetClassName,
|
||||
instrumentationClassName);
|
||||
return mci;
|
||||
}
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
private boolean isInstrumentationMethod(String name, String desc) {
|
||||
for(Method m : instrumentationMethods) {
|
||||
if (m.getName().equals(name) && Type.getMethodDescriptor(m).equals(desc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MethodNode findTargetMethodNode(String name, String desc) {
|
||||
for (MethodNode mn : targetClassNode.methods) {
|
||||
if (mn.desc.equals(desc) && mn.name.equals(name)) {
|
||||
return mn;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("could not find MethodNode for "
|
||||
+ name + desc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface JIInstrumentationMethod {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface JIInstrumentationTarget {
|
||||
String value();
|
||||
}
|
||||
149
jdkSrc/jdk8/jdk/jfr/internal/instrument/JIMethodCallInliner.java
Normal file
149
jdkSrc/jdk8/jdk/jfr/internal/instrument/JIMethodCallInliner.java
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.commons.LocalVariablesSorter;
|
||||
import jdk.internal.org.objectweb.asm.commons.Remapper;
|
||||
import jdk.internal.org.objectweb.asm.commons.SimpleRemapper;
|
||||
import jdk.internal.org.objectweb.asm.tree.MethodNode;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
|
||||
/**
|
||||
* Class responsible for finding the call to inline and inlining it.
|
||||
*
|
||||
* This code is heavily influenced by section 3.2.6 "Inline Method" in
|
||||
* "Using ASM framework to implement common bytecode transformation patterns",
|
||||
* E. Kuleshov, AOSD.07, March 2007, Vancouver, Canada.
|
||||
* http://asm.ow2.org/index.html
|
||||
*/
|
||||
@Deprecated
|
||||
final class JIMethodCallInliner extends LocalVariablesSorter {
|
||||
|
||||
private final String oldClass;
|
||||
private final String newClass;
|
||||
private final MethodNode inlineTarget;
|
||||
private final List<CatchBlock> blocks = new ArrayList<>();
|
||||
private boolean inlining;
|
||||
|
||||
/**
|
||||
* inlineTarget defines the method to inline and also contains the actual
|
||||
* code to inline.
|
||||
*
|
||||
* @param access
|
||||
* @param desc
|
||||
* @param mv
|
||||
* @param inlineTarget
|
||||
* @param oldClass
|
||||
* @param newClass
|
||||
* @param logger
|
||||
*/
|
||||
public JIMethodCallInliner(int access, String desc, MethodVisitor mv,
|
||||
MethodNode inlineTarget, String oldClass, String newClass) {
|
||||
super(Opcodes.ASM5, access, desc, mv);
|
||||
this.oldClass = oldClass;
|
||||
this.newClass = newClass;
|
||||
this.inlineTarget = inlineTarget;
|
||||
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "MethodCallInliner: targetMethod=" + newClass + "."
|
||||
+ inlineTarget.name + inlineTarget.desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name,
|
||||
String desc, boolean itf) {
|
||||
// Now we are looking at method call in the source method
|
||||
if (!shouldBeInlined(owner, name, desc)) {
|
||||
// If this method call should not be inlined, just keep it
|
||||
mv.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
return;
|
||||
}
|
||||
// If the call should be inlined, we create a MethodInliningAdapter
|
||||
// The MIA will walk the instructions in the inlineTarget and add them
|
||||
// to the current method, doing the necessary name remappings.
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Inlining call to " + name + desc);
|
||||
Remapper remapper = new SimpleRemapper(oldClass, newClass);
|
||||
Label end = new Label();
|
||||
inlining = true;
|
||||
inlineTarget.instructions.resetLabels();
|
||||
JIMethodInliningAdapter mia = new JIMethodInliningAdapter(this, end,
|
||||
opcode == Opcodes.INVOKESTATIC ? Opcodes.ACC_STATIC : 0, desc,
|
||||
remapper);
|
||||
inlineTarget.accept(mia);
|
||||
inlining = false;
|
||||
super.visitLabel(end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the method should be inlined or not.
|
||||
*/
|
||||
private boolean shouldBeInlined(String owner, String name, String desc) {
|
||||
return inlineTarget.desc.equals(desc) && inlineTarget.name.equals(name)
|
||||
&& owner.equals(newClass.replace('.', '/'));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler,
|
||||
String type) {
|
||||
if (!inlining) {
|
||||
// try-catch blocks are saved here and replayed at the end
|
||||
// of the method (in visitMaxs)
|
||||
blocks.add(new CatchBlock(start, end, handler, type));
|
||||
} else {
|
||||
super.visitTryCatchBlock(start, end, handler, type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int stack, int locals) {
|
||||
for (CatchBlock b : blocks) {
|
||||
super.visitTryCatchBlock(b.start, b.end, b.handler, b.type);
|
||||
}
|
||||
super.visitMaxs(stack, locals);
|
||||
}
|
||||
|
||||
static final class CatchBlock {
|
||||
|
||||
final Label start;
|
||||
final Label end;
|
||||
final Label handler;
|
||||
final String type;
|
||||
|
||||
CatchBlock(Label start, Label end, Label handler, String type) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.handler = handler;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.LocalVariablesSorter;
|
||||
import jdk.internal.org.objectweb.asm.commons.Remapper;
|
||||
import jdk.internal.org.objectweb.asm.commons.RemappingMethodAdapter;
|
||||
|
||||
@Deprecated
|
||||
final class JIMethodInliningAdapter extends RemappingMethodAdapter {
|
||||
private final LocalVariablesSorter lvs;
|
||||
private final Label end;
|
||||
|
||||
public JIMethodInliningAdapter(LocalVariablesSorter mv, Label end, int acc, String desc, Remapper remapper) {
|
||||
super(acc, desc, mv, remapper);
|
||||
this.lvs = mv;
|
||||
this.end = end;
|
||||
int offset = isStatic(acc) ? 0 : 1;
|
||||
Type[] args = Type.getArgumentTypes(desc);
|
||||
for (int i = args.length - 1; i >= 0; i--) {
|
||||
super.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), i + offset);
|
||||
}
|
||||
if (offset > 0) {
|
||||
super.visitVarInsn(Opcodes.ASTORE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStatic(int acc) {
|
||||
return (acc & Opcodes.ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode) {
|
||||
if (opcode == Opcodes.RETURN || opcode == Opcodes.IRETURN
|
||||
|| opcode == Opcodes.ARETURN || opcode == Opcodes.LRETURN) {
|
||||
super.visitJumpInsn(Opcodes.GOTO, end);
|
||||
} else {
|
||||
super.visitInsn(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int stack, int locals) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int newLocalMapping(Type type) {
|
||||
return lvs.newLocal(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.commons.RemappingMethodAdapter;
|
||||
import jdk.internal.org.objectweb.asm.commons.SimpleRemapper;
|
||||
import jdk.internal.org.objectweb.asm.tree.ClassNode;
|
||||
import jdk.internal.org.objectweb.asm.tree.MethodNode;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
|
||||
/**
|
||||
* This class will merge (some) methods from one class into another one.
|
||||
*
|
||||
* @author Staffan Larsen
|
||||
*/
|
||||
@Deprecated
|
||||
final class JIMethodMergeAdapter extends ClassVisitor {
|
||||
|
||||
private final ClassNode cn;
|
||||
private final List<Method> methodFilter;
|
||||
private final Map<String, String> typeMap;
|
||||
|
||||
/**
|
||||
* Methods in methodFilter that exist in cn will be merged into cv. If the method already exists,
|
||||
* the original method will be deleted.
|
||||
*
|
||||
* @param cv
|
||||
* @param cn - a ClassNode with Methods that will be merged into this class
|
||||
* @param methodFilter - only methods in this list will be merged
|
||||
* @param typeMappings - while merging, type references in the methods will be changed according to this map
|
||||
*/
|
||||
public JIMethodMergeAdapter(ClassVisitor cv, ClassNode cn, List<Method> methodFilter, JITypeMapping[] typeMappings) {
|
||||
super(Opcodes.ASM5, cv);
|
||||
this.cn = cn;
|
||||
this.methodFilter = methodFilter;
|
||||
|
||||
this.typeMap = new HashMap<>();
|
||||
for (JITypeMapping tm : typeMappings) {
|
||||
typeMap.put(tm.from().replace('.', '/'), tm.to().replace('.', '/'));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
typeMap.put(cn.name, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if(methodInFilter(name, desc)) {
|
||||
// If the method is one that we will be replacing, delete the method
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Deleting " + name + desc);
|
||||
return null;
|
||||
}
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
SimpleRemapper remapper = new SimpleRemapper(typeMap);
|
||||
for (MethodNode mn : cn.methods) {
|
||||
// Check if the method is in the list of methods to copy
|
||||
if (methodInFilter(mn.name, mn.desc)) {
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Copying method: " + mn.name + mn.desc);
|
||||
Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, " with mapper: " + typeMap);
|
||||
|
||||
String[] exceptions = new String[mn.exceptions.size()];
|
||||
mn.exceptions.toArray(exceptions);
|
||||
MethodVisitor mv = cv.visitMethod(mn.access, mn.name, mn.desc, mn.signature, exceptions);
|
||||
mn.instructions.resetLabels();
|
||||
mn.accept(new RemappingMethodAdapter(mn.access, mn.desc, mv, remapper));
|
||||
}
|
||||
}
|
||||
super.visitEnd();
|
||||
}
|
||||
|
||||
private boolean methodInFilter(String name, String desc) {
|
||||
for(Method m : methodFilter) {
|
||||
if (m.getName().equals(name) && Type.getMethodDescriptor(m).equals(desc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
35
jdkSrc/jdk8/jdk/jfr/internal/instrument/JITypeMapping.java
Normal file
35
jdkSrc/jdk8/jdk/jfr/internal/instrument/JITypeMapping.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface JITypeMapping {
|
||||
String from();
|
||||
String to();
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jdk.jfr.events.FileReadEvent;
|
||||
import jdk.jfr.events.FileWriteEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("java.io.RandomAccessFile")
|
||||
final class RandomAccessFileInstrumentor {
|
||||
|
||||
private RandomAccessFileInstrumentor() {
|
||||
}
|
||||
|
||||
private String path;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read() throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read();
|
||||
}
|
||||
int result = 0;
|
||||
try {
|
||||
event.begin();
|
||||
result = read();
|
||||
if (result < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = 1;
|
||||
}
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(byte b[]) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(b);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(b);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
FileReadEvent event = FileReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(b, off, len);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(b, off, len);
|
||||
} finally {
|
||||
if (bytesRead < 0) {
|
||||
event.endOfFile = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void write(int b) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
write(b);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
write(b);
|
||||
event.bytesWritten = 1;
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void write(byte b[]) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
write(b);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
write(b);
|
||||
event.bytesWritten = b.length;
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
FileWriteEvent event = FileWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
write(b, off, len);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
event.begin();
|
||||
write(b, off, len);
|
||||
event.bytesWritten = len;
|
||||
} finally {
|
||||
event.path = path;
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.jfr.events.SocketReadEvent;
|
||||
import jdk.jfr.events.SocketWriteEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("sun.nio.ch.SocketChannelImpl")
|
||||
final class SocketChannelImplInstrumentor {
|
||||
|
||||
private SocketChannelImplInstrumentor() {
|
||||
}
|
||||
|
||||
private InetSocketAddress remoteAddress;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
SocketReadEvent event = SocketReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(dst);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(dst);
|
||||
} finally {
|
||||
event.end();
|
||||
if (event.shouldCommit()) {
|
||||
String hostString = remoteAddress.getAddress().toString();
|
||||
int delimiterIndex = hostString.lastIndexOf('/');
|
||||
|
||||
event.host = hostString.substring(0, delimiterIndex);
|
||||
event.address = hostString.substring(delimiterIndex + 1);
|
||||
event.port = remoteAddress.getPort();
|
||||
if (bytesRead < 0) {
|
||||
event.endOfStream = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.timeout = 0;
|
||||
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
|
||||
SocketReadEvent event = SocketReadEvent.EVENT.get();
|
||||
if(!event.isEnabled()) {
|
||||
return read(dsts, offset, length);
|
||||
}
|
||||
|
||||
long bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(dsts, offset, length);
|
||||
} finally {
|
||||
event.end();
|
||||
if (event.shouldCommit()) {
|
||||
String hostString = remoteAddress.getAddress().toString();
|
||||
int delimiterIndex = hostString.lastIndexOf('/');
|
||||
|
||||
event.host = hostString.substring(0, delimiterIndex);
|
||||
event.address = hostString.substring(delimiterIndex + 1);
|
||||
event.port = remoteAddress.getPort();
|
||||
if (bytesRead < 0) {
|
||||
event.endOfStream = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.timeout = 0;
|
||||
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public int write(ByteBuffer buf) throws IOException {
|
||||
SocketWriteEvent event = SocketWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return write(buf);
|
||||
}
|
||||
|
||||
int bytesWritten = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesWritten = write(buf);
|
||||
} finally {
|
||||
event.end();
|
||||
if (event.shouldCommit()) {
|
||||
String hostString = remoteAddress.getAddress().toString();
|
||||
int delimiterIndex = hostString.lastIndexOf('/');
|
||||
|
||||
event.host = hostString.substring(0, delimiterIndex);
|
||||
event.address = hostString.substring(delimiterIndex + 1);
|
||||
event.port = remoteAddress.getPort();
|
||||
event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten;
|
||||
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
|
||||
SocketWriteEvent event = SocketWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return write(srcs, offset, length);
|
||||
}
|
||||
long bytesWritten = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesWritten = write(srcs, offset, length);
|
||||
} finally {
|
||||
event.end();
|
||||
if (event.shouldCommit()) {
|
||||
String hostString = remoteAddress.getAddress().toString();
|
||||
int delimiterIndex = hostString.lastIndexOf('/');
|
||||
|
||||
event.host = hostString.substring(0, delimiterIndex);
|
||||
event.address = hostString.substring(delimiterIndex + 1);
|
||||
event.port = remoteAddress.getPort();
|
||||
event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten;
|
||||
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import jdk.jfr.events.SocketReadEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("java.net.SocketInputStream")
|
||||
@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketInputStreamInstrumentor$AbstractPlainSocketImpl",
|
||||
to = "java.net.AbstractPlainSocketImpl")
|
||||
final class SocketInputStreamInstrumentor {
|
||||
|
||||
private SocketInputStreamInstrumentor() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
int read(byte b[], int off, int length, int timeout) throws IOException {
|
||||
SocketReadEvent event = SocketReadEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
return read(b, off, length, timeout);
|
||||
}
|
||||
int bytesRead = 0;
|
||||
try {
|
||||
event.begin();
|
||||
bytesRead = read(b, off, length, timeout);
|
||||
} finally {
|
||||
event.end();
|
||||
if (event.shouldCommit()) {
|
||||
String hostString = impl.address.toString();
|
||||
int delimiterIndex = hostString.lastIndexOf('/');
|
||||
|
||||
event.host = hostString.substring(0, delimiterIndex);
|
||||
event.address = hostString.substring(delimiterIndex + 1);
|
||||
event.port = impl.port;
|
||||
if (bytesRead < 0) {
|
||||
event.endOfStream = true;
|
||||
} else {
|
||||
event.bytesRead = bytesRead;
|
||||
}
|
||||
event.timeout = timeout;
|
||||
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
private AbstractPlainSocketImpl impl = null;
|
||||
|
||||
void silenceFindBugsUnwrittenField(InetAddress dummy) {
|
||||
impl.address = dummy;
|
||||
}
|
||||
|
||||
static class AbstractPlainSocketImpl {
|
||||
InetAddress address;
|
||||
int port;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.instrument;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import jdk.jfr.events.SocketWriteEvent;
|
||||
|
||||
/**
|
||||
* See {@link JITracer} for an explanation of this code.
|
||||
*/
|
||||
@JIInstrumentationTarget("java.net.SocketOutputStream")
|
||||
@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketOutputStreamInstrumentor$AbstractPlainSocketImpl",
|
||||
to = "java.net.AbstractPlainSocketImpl")
|
||||
final class SocketOutputStreamInstrumentor {
|
||||
|
||||
private SocketOutputStreamInstrumentor() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@JIInstrumentationMethod
|
||||
private void socketWrite(byte b[], int off, int len) throws IOException {
|
||||
SocketWriteEvent event = SocketWriteEvent.EVENT.get();
|
||||
if (!event.isEnabled()) {
|
||||
socketWrite(b, off, len);
|
||||
return;
|
||||
}
|
||||
int bytesWritten = 0;
|
||||
try {
|
||||
event.begin();
|
||||
socketWrite(b, off, len);
|
||||
bytesWritten = len;
|
||||
} finally {
|
||||
event.end() ;
|
||||
if (event.shouldCommit()) {
|
||||
String hostString = impl.address.toString();
|
||||
int delimiterIndex = hostString.lastIndexOf('/');
|
||||
|
||||
event.host = hostString.substring(0, delimiterIndex);
|
||||
event.address = hostString.substring(delimiterIndex + 1);
|
||||
event.port = impl.port;
|
||||
event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten;
|
||||
|
||||
event.commit();
|
||||
event.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractPlainSocketImpl impl = null;
|
||||
|
||||
void silenceFindBugsUnwrittenField(InetAddress dummy) {
|
||||
impl.address = dummy;
|
||||
}
|
||||
|
||||
static class AbstractPlainSocketImpl {
|
||||
InetAddress address;
|
||||
int port;
|
||||
}
|
||||
}
|
||||
64
jdkSrc/jdk8/jdk/jfr/internal/instrument/ThrowableTracer.java
Normal file
64
jdkSrc/jdk8/jdk/jfr/internal/instrument/ThrowableTracer.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.instrument;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import jdk.jfr.events.ErrorThrownEvent;
|
||||
import jdk.jfr.events.ExceptionThrownEvent;
|
||||
|
||||
public final class ThrowableTracer {
|
||||
|
||||
private static AtomicLong numThrowables = new AtomicLong(0);
|
||||
|
||||
public static void traceError(Error e, String message) {
|
||||
if (e instanceof OutOfMemoryError) {
|
||||
return;
|
||||
}
|
||||
ErrorThrownEvent errorEvent = new ErrorThrownEvent();
|
||||
errorEvent.message = message;
|
||||
errorEvent.thrownClass = e.getClass();
|
||||
errorEvent.commit();
|
||||
|
||||
ExceptionThrownEvent exceptionEvent = new ExceptionThrownEvent();
|
||||
exceptionEvent.message = message;
|
||||
exceptionEvent.thrownClass = e.getClass();
|
||||
exceptionEvent.commit();
|
||||
numThrowables.incrementAndGet();
|
||||
}
|
||||
|
||||
public static void traceThrowable(Throwable t, String message) {
|
||||
ExceptionThrownEvent event = new ExceptionThrownEvent();
|
||||
event.message = message;
|
||||
event.thrownClass = t.getClass();
|
||||
event.commit();
|
||||
numThrowables.incrementAndGet();
|
||||
}
|
||||
|
||||
public static long numThrowables() {
|
||||
return numThrowables.get();
|
||||
}
|
||||
}
|
||||
247
jdkSrc/jdk8/jdk/jfr/internal/jfc/JFC.java
Normal file
247
jdkSrc/jdk8/jdk/jfr/internal/jfc/JFC.java
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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.jfc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.Configuration;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
/**
|
||||
* {@link Configuration} factory for JFC files. *
|
||||
*/
|
||||
public final class JFC {
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
private static final int MAXIMUM_FILE_SIZE = 1024 * 1024;
|
||||
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
|
||||
private static volatile List<KnownConfiguration> knownConfigurations;
|
||||
|
||||
/**
|
||||
* Reads a known configuration file (located into a string, but doesn't
|
||||
* parse it until it's being used.
|
||||
*/
|
||||
private static final class KnownConfiguration {
|
||||
private final String content;
|
||||
private final String filename;
|
||||
private final String name;
|
||||
private Configuration configuration;
|
||||
|
||||
public KnownConfiguration(SafePath knownPath) throws IOException {
|
||||
this.content = readContent(knownPath);
|
||||
this.name = nameFromPath(knownPath.toPath());
|
||||
this.filename = nullSafeFileName(knownPath.toPath());
|
||||
}
|
||||
|
||||
public boolean isNamed(String name) {
|
||||
return filename.equals(name) || this.name.equals(name);
|
||||
}
|
||||
|
||||
public Configuration getConfigurationFile() throws IOException, ParseException {
|
||||
if (configuration == null) {
|
||||
configuration = JFCParser.createConfiguration(name, content);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private static String readContent(SafePath knownPath) throws IOException {
|
||||
if (SecuritySupport.getFileSize(knownPath) > MAXIMUM_FILE_SIZE) {
|
||||
throw new IOException("Configuration with more than "
|
||||
+ MAXIMUM_FILE_SIZE + " characters can't be read.");
|
||||
}
|
||||
try (InputStream r = SecuritySupport.newFileInputStream(knownPath)) {
|
||||
return JFC.readContent(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JFC() {
|
||||
// private utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a configuration from a file.
|
||||
*
|
||||
* @param path the file containing the configuration, not {@code null}
|
||||
* @return {@link Configuration}, not {@code null}
|
||||
* @throws ParseException if the file can't be parsed
|
||||
* @throws IOException if the file can't be read
|
||||
*
|
||||
* @throws SecurityException if a security manager exists and its
|
||||
* <code>checkRead</code> method denies read access to the file.
|
||||
* @see java.io.File#getPath()
|
||||
* @see java.lang.SecurityManager#checkRead(java.lang.String)
|
||||
*/
|
||||
public static Configuration create(String name, Reader reader) throws IOException, ParseException {
|
||||
return JFCParser.createConfiguration(name, reader);
|
||||
}
|
||||
|
||||
private static String nullSafeFileName(Path file) throws IOException {
|
||||
Path filename = file.getFileName();
|
||||
if (filename == null) {
|
||||
throw new IOException("Path has no file name");
|
||||
}
|
||||
return filename.toString();
|
||||
}
|
||||
|
||||
public static String nameFromPath(Path file) throws IOException {
|
||||
String f = nullSafeFileName(file);
|
||||
if (f.endsWith(JFCParser.FILE_EXTENSION)) {
|
||||
return f.substring(0, f.length() - JFCParser.FILE_EXTENSION.length());
|
||||
} else {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked by DCmdStart
|
||||
public static Configuration createKnown(String name) throws IOException, ParseException {
|
||||
// Known name, no need for permission
|
||||
for (KnownConfiguration known : getKnownConfigurations()) {
|
||||
if (known.isNamed(name)) {
|
||||
return known.getConfigurationFile();
|
||||
}
|
||||
}
|
||||
// Check JFC directory
|
||||
SafePath path = SecuritySupport.JFC_DIRECTORY;
|
||||
if (path != null && SecuritySupport.exists(path)) {
|
||||
for (String extension : Arrays.asList("", JFCParser.FILE_EXTENSION)) {
|
||||
SafePath file = new SafePath(path.toPath().resolveSibling(name + extension));
|
||||
if (SecuritySupport.exists(file) && !SecuritySupport.isDirectory(file)) {
|
||||
try (Reader r = SecuritySupport.newFileReader(file)) {
|
||||
String jfcName = nameFromPath(file.toPath());
|
||||
return JFCParser.createConfiguration(jfcName, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assume path included in name
|
||||
|
||||
Path localPath = Paths.get(name);
|
||||
String jfcName = nameFromPath(localPath);
|
||||
try (Reader r = Files.newBufferedReader(localPath)) {
|
||||
return JFCParser.createConfiguration(jfcName, r);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readContent(InputStream source) throws IOException {
|
||||
byte[] bytes = read(source, BUFFER_SIZE);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
// copied from java.io.file.Files to avoid dependency on JDK 9 code
|
||||
private static byte[] read(InputStream source, int initialSize) throws IOException {
|
||||
int capacity = initialSize;
|
||||
byte[] buf = new byte[capacity];
|
||||
int nread = 0;
|
||||
int n;
|
||||
for (;;) {
|
||||
// read to EOF which may read more or less than initialSize (eg: file
|
||||
// is truncated while we are reading)
|
||||
while ((n = source.read(buf, nread, capacity - nread)) > 0)
|
||||
nread += n;
|
||||
|
||||
// if last call to source.read() returned -1, we are done
|
||||
// otherwise, try to read one more byte; if that failed we're done too
|
||||
if (n < 0 || (n = source.read()) < 0)
|
||||
break;
|
||||
|
||||
// one more byte was read; need to allocate a larger buffer
|
||||
if (capacity <= MAX_BUFFER_SIZE - capacity) {
|
||||
capacity = Math.max(capacity << 1, BUFFER_SIZE);
|
||||
} else {
|
||||
if (capacity == MAX_BUFFER_SIZE)
|
||||
throw new OutOfMemoryError("Required array size too large");
|
||||
capacity = MAX_BUFFER_SIZE;
|
||||
}
|
||||
buf = Arrays.copyOf(buf, capacity);
|
||||
buf[nread++] = (byte)n;
|
||||
}
|
||||
return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns list of predefined configurations available.
|
||||
*
|
||||
* @return list of configurations, not null
|
||||
*/
|
||||
public static List<Configuration> getConfigurations() {
|
||||
List<Configuration> configs = new ArrayList<>();
|
||||
for (KnownConfiguration knownConfig : getKnownConfigurations()) {
|
||||
try {
|
||||
configs.add(knownConfig.getConfigurationFile());
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not load configuration " + knownConfig.getName() + ". " + e.getMessage());
|
||||
} catch (ParseException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not parse configuration " + knownConfig.getName() + ". " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
private static List<KnownConfiguration> getKnownConfigurations() {
|
||||
if (knownConfigurations == null) {
|
||||
List<KnownConfiguration> configProxies = new ArrayList<>();
|
||||
for (SafePath p : SecuritySupport.getPredefinedJFCFiles()) {
|
||||
try {
|
||||
configProxies.add(new KnownConfiguration(p));
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
knownConfigurations = configProxies;
|
||||
}
|
||||
return knownConfigurations;
|
||||
}
|
||||
|
||||
public static Configuration getPredefined(String name) throws IOException, ParseException {
|
||||
for (KnownConfiguration knownConfig : getKnownConfigurations()) {
|
||||
if (knownConfig.getName().equals(name)) {
|
||||
return knownConfig.getConfigurationFile();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFileException("Could not locate configuration with name " + name);
|
||||
}
|
||||
}
|
||||
85
jdkSrc/jdk8/jdk/jfr/internal/jfc/JFCParser.java
Normal file
85
jdkSrc/jdk8/jdk/jfr/internal/jfc/JFCParser.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.jfc;
|
||||
|
||||
import java.io.CharArrayReader;
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.text.ParseException;
|
||||
import jdk.internal.org.xml.sax.InputSource;
|
||||
import jdk.internal.org.xml.sax.SAXException;
|
||||
import jdk.internal.util.xml.SAXParser;
|
||||
import jdk.internal.util.xml.impl.SAXParserImpl;
|
||||
import jdk.jfr.Configuration;
|
||||
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
|
||||
/**
|
||||
* Parses a JDK Flight Recorder Configuration file (.jfc)
|
||||
*/
|
||||
final class JFCParser {
|
||||
static final String FILE_EXTENSION = ".jfc";
|
||||
private static final int MAXIMUM_FILE_SIZE = 1024 * 1024;
|
||||
|
||||
public static Configuration createConfiguration(String name, Reader reader) throws IOException, ParseException {
|
||||
return createConfiguration(name, readContent(reader));
|
||||
}
|
||||
|
||||
public static Configuration createConfiguration(String name, String content) throws IOException, ParseException {
|
||||
try {
|
||||
JFCParserHandler ch = new JFCParserHandler();
|
||||
parseXML(content, ch);
|
||||
return PrivateAccess.getInstance().newConfiguration(name, ch.label, ch.description, ch.provider, ch.settings, content);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new ParseException(iae.getMessage(), -1);
|
||||
} catch (SAXException e) {
|
||||
ParseException pe = new ParseException("Error reading JFC file. " + e.getMessage(), -1);
|
||||
pe.initCause(e);
|
||||
throw pe;
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseXML(String content, JFCParserHandler ch) throws SAXException, IOException {
|
||||
CharArrayReader r = new CharArrayReader(content.toCharArray());
|
||||
SAXParser parser = new SAXParserImpl();
|
||||
parser.parse(new InputSource(r), ch);
|
||||
}
|
||||
|
||||
private static String readContent(Reader r) throws IOException {
|
||||
CharArrayWriter writer = new CharArrayWriter(1024);
|
||||
int count = 0;
|
||||
int ch;
|
||||
while ((ch = r.read()) != -1) {
|
||||
writer.write(ch);
|
||||
count++;
|
||||
if (count >= MAXIMUM_FILE_SIZE) {
|
||||
throw new IOException("Presets with more than " + MAXIMUM_FILE_SIZE + " characters can't be read.");
|
||||
}
|
||||
}
|
||||
return new String(writer.toCharArray());
|
||||
}
|
||||
}
|
||||
106
jdkSrc/jdk8/jdk/jfr/internal/jfc/JFCParserHandler.java
Normal file
106
jdkSrc/jdk8/jdk/jfr/internal/jfc/JFCParserHandler.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.jfc;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.org.xml.sax.Attributes;
|
||||
import jdk.internal.org.xml.sax.SAXException;
|
||||
import jdk.internal.org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
final class JFCParserHandler extends DefaultHandler {
|
||||
private static final String ELEMENT_CONFIGURATION = "configuration";
|
||||
private static final String ELEMENT_EVENT_TYPE = "event";
|
||||
private static final String ELEMENT_SETTING = "setting";
|
||||
private static final String ATTRIBUTE_NAME = "name";
|
||||
private static final String ATTRIBUTE_LABEL = "label";
|
||||
private static final String ATTRIBUTE_DESCRIPTION = "description";
|
||||
private static final String ATTRIBUTE_PROVIDER = "provider";
|
||||
private static final String ATTRIBUTE_VERSION = "version";
|
||||
|
||||
final Map<String, String> settings = new LinkedHashMap<String, String>();
|
||||
private String currentEventPath;
|
||||
private String currentSettingsName;
|
||||
private StringBuilder currentCharacters;
|
||||
String label;
|
||||
String provider;
|
||||
String description;
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
switch (qName.toLowerCase()) {
|
||||
case ELEMENT_CONFIGURATION:
|
||||
String version = attributes.getValue(ATTRIBUTE_VERSION);
|
||||
if (version == null || !version.startsWith("2.")) {
|
||||
throw new SAXException("This version of Flight Recorder can only read JFC file format version 2.x");
|
||||
}
|
||||
label = attributes.getValue(ATTRIBUTE_LABEL);
|
||||
description = getOptional(attributes, ATTRIBUTE_DESCRIPTION, "");
|
||||
provider = getOptional(attributes, ATTRIBUTE_PROVIDER, "");
|
||||
break;
|
||||
case ELEMENT_EVENT_TYPE:
|
||||
currentEventPath = attributes.getValue(ATTRIBUTE_NAME);
|
||||
break;
|
||||
case ELEMENT_SETTING:
|
||||
currentSettingsName = attributes.getValue(ATTRIBUTE_NAME);
|
||||
break;
|
||||
}
|
||||
currentCharacters = null;
|
||||
}
|
||||
|
||||
private String getOptional(Attributes attributes, String name, String defaultValue) {
|
||||
String value = attributes.getValue(name);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
if (currentCharacters == null) {
|
||||
currentCharacters = new StringBuilder(length);
|
||||
}
|
||||
currentCharacters.append(ch, start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) {
|
||||
switch (qName.toLowerCase()) {
|
||||
case ELEMENT_CONFIGURATION:
|
||||
break;
|
||||
case ELEMENT_EVENT_TYPE:
|
||||
currentEventPath = null;
|
||||
break;
|
||||
case ELEMENT_SETTING:
|
||||
String settingsValue = currentCharacters == null ? "" : currentCharacters.toString();
|
||||
settings.put(currentEventPath + "#" + currentSettingsName, "" + settingsValue);
|
||||
currentSettingsName = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getSettings() {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
31
jdkSrc/jdk8/jdk/jfr/internal/jfc/package-info.java
Normal file
31
jdkSrc/jdk8/jdk/jfr/internal/jfc/package-info.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package contains classes for configuring Flight Recorder using JFC-files.
|
||||
*
|
||||
* @since 8
|
||||
*/
|
||||
package jdk.jfr.internal.jfc;
|
||||
101
jdkSrc/jdk8/jdk/jfr/internal/management/ManagementSupport.java
Normal file
101
jdkSrc/jdk8/jdk/jfr/internal/management/ManagementSupport.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.management;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.JVMSupport;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.MetadataRepository;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.Utils;
|
||||
import jdk.jfr.internal.WriteableUserPath;
|
||||
import jdk.jfr.internal.instrument.JDKEvents;
|
||||
|
||||
/**
|
||||
* The management API in module jdk.management.jfr should be built on top of the
|
||||
* public API in jdk.jfr. Before putting more functionality here, consider if it
|
||||
* should not be part of the public API, and if not, please provide motivation
|
||||
*
|
||||
*/
|
||||
public final class ManagementSupport {
|
||||
|
||||
// Purpose of this method is to expose the event types to the
|
||||
// FlightRecorderMXBean without instantiating Flight Recorder.
|
||||
//
|
||||
// This allows:
|
||||
//
|
||||
// 1) discoverability, so event settings can be exposed without the need to
|
||||
// create a new Recording in FlightrecorderMXBean.
|
||||
//
|
||||
// 2) a graphical JMX client to list all attributes to the user, without
|
||||
// loading JFR memory buffers. This is especially important when there is
|
||||
// no intent to use Flight Recorder.
|
||||
//
|
||||
// An alternative design would be to make FlightRecorder#getEventTypes
|
||||
// static, but it would the make the API look strange
|
||||
//
|
||||
public static List<EventType> getEventTypes() {
|
||||
// would normally be checked when a Flight Recorder instance is created
|
||||
Utils.checkAccessFlightRecorder();
|
||||
if (JVMSupport.isNotAvailable()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
JDKEvents.initialize(); // make sure JDK events are available
|
||||
return Collections.unmodifiableList(MetadataRepository.getInstance().getRegisteredEventTypes());
|
||||
}
|
||||
|
||||
// Reuse internal code for parsing a timespan
|
||||
public static long parseTimespan(String s) {
|
||||
return Utils.parseTimespan(s);
|
||||
}
|
||||
|
||||
// Reuse internal code for formatting settings
|
||||
public static final String formatTimespan(Duration dValue, String separation) {
|
||||
return Utils.formatTimespan(dValue, separation);
|
||||
}
|
||||
|
||||
// Reuse internal logging mechanism
|
||||
public static void logError(String message) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, message);
|
||||
}
|
||||
|
||||
// Get the textual representation when the destination was set, which
|
||||
// requires access to jdk.jfr.internal.PlatformRecording
|
||||
public static String getDestinationOriginalText(Recording recording) {
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
WriteableUserPath wup = pr.getDestination();
|
||||
return wup == null ? null : wup.getOriginalText();
|
||||
}
|
||||
}
|
||||
74
jdkSrc/jdk8/jdk/jfr/internal/settings/BooleanValue.java
Normal file
74
jdkSrc/jdk8/jdk/jfr/internal/settings/BooleanValue.java
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helper class for settings that use boolean numbers
|
||||
*
|
||||
*/
|
||||
final class BooleanValue {
|
||||
private String value = "false";
|
||||
private boolean booleanValue;
|
||||
|
||||
private BooleanValue(boolean b) {
|
||||
booleanValue = b;
|
||||
value = b ? "true" : "false";
|
||||
}
|
||||
|
||||
public String union(Set<String> values) {
|
||||
for (String v : values) {
|
||||
if ("true".equals(v)) {
|
||||
return "true";
|
||||
}
|
||||
}
|
||||
return "false";
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
this.booleanValue = Boolean.valueOf(value);
|
||||
}
|
||||
|
||||
public final String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public boolean getBoolean() {
|
||||
return booleanValue;
|
||||
}
|
||||
|
||||
public static BooleanValue valueOf(String defaultValue) {
|
||||
if ("true".equals(defaultValue)) {
|
||||
return new BooleanValue(true);
|
||||
}
|
||||
if ("false".equals(defaultValue)) {
|
||||
return new BooleanValue(false);
|
||||
}
|
||||
throw new InternalError("Unknown default value for settings '" + defaultValue + "'");
|
||||
}
|
||||
}
|
||||
97
jdkSrc/jdk8/jdk/jfr/internal/settings/CutoffSetting.java
Normal file
97
jdkSrc/jdk8/jdk/jfr/internal/settings/CutoffSetting.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 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.settings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.internal.Control;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
@MetadataDefinition
|
||||
@Label("Cutoff")
|
||||
@Description("Limit running time of event")
|
||||
@Name(Type.SETTINGS_PREFIX + "Cutoff")
|
||||
@Timespan
|
||||
public final class CutoffSetting extends Control {
|
||||
private final static long typeId = Type.getTypeId(CutoffSetting.class);
|
||||
|
||||
private String value = "0 ns";
|
||||
private final PlatformEventType eventType;
|
||||
|
||||
public CutoffSetting(PlatformEventType eventType, String defaultValue) {
|
||||
super(defaultValue);
|
||||
this.eventType = Objects.requireNonNull(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Set<String> values) {
|
||||
long max = 0;
|
||||
String text = "0 ns";
|
||||
for (String value : values) {
|
||||
long l = Utils.parseTimespanWithInfinity(value);
|
||||
if (l > max) {
|
||||
text = value;
|
||||
max = l;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
long l = Utils.parseTimespanWithInfinity(value);
|
||||
this.value = value;
|
||||
eventType.setCutoff(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean isType(long typeId) {
|
||||
return CutoffSetting.typeId == typeId;
|
||||
}
|
||||
|
||||
public static long parseValueSafe(String value) {
|
||||
if (value == null) {
|
||||
return 0L;
|
||||
}
|
||||
try {
|
||||
return Utils.parseTimespanWithInfinity(value);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
jdkSrc/jdk8/jdk/jfr/internal/settings/EnabledSetting.java
Normal file
75
jdkSrc/jdk8/jdk/jfr/internal/settings/EnabledSetting.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.BooleanFlag;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.Control;
|
||||
|
||||
@MetadataDefinition
|
||||
@Label("Enabled")
|
||||
@Description("Record event")
|
||||
@Name(Type.SETTINGS_PREFIX + "Enabled")
|
||||
@BooleanFlag
|
||||
public final class EnabledSetting extends Control {
|
||||
private final BooleanValue booleanValue;
|
||||
private final PlatformEventType eventType;
|
||||
|
||||
public EnabledSetting(PlatformEventType eventType, String defaultValue) {
|
||||
super(defaultValue);
|
||||
this.booleanValue = BooleanValue.valueOf(defaultValue);
|
||||
this.eventType = Objects.requireNonNull(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Set<String> values) {
|
||||
return booleanValue.union(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
booleanValue.setValue(value);
|
||||
eventType.setEnabled(booleanValue.getBoolean());
|
||||
if (eventType.isEnabled() && !eventType.isJVM()) {
|
||||
if (!eventType.isInstrumented()) {
|
||||
eventType.markForInstrumentation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return booleanValue.getValue();
|
||||
}
|
||||
}
|
||||
136
jdkSrc/jdk8/jdk/jfr/internal/settings/PeriodSetting.java
Normal file
136
jdkSrc/jdk8/jdk/jfr/internal/settings/PeriodSetting.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Control;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
@MetadataDefinition
|
||||
@Label("Period")
|
||||
@Description("Record event at interval")
|
||||
@Name(Type.SETTINGS_PREFIX + "Period")
|
||||
public final class PeriodSetting extends Control {
|
||||
private static final long typeId = Type.getTypeId(PeriodSetting.class);
|
||||
|
||||
public static final String EVERY_CHUNK = "everyChunk";
|
||||
public static final String BEGIN_CHUNK = "beginChunk";
|
||||
public static final String END_CHUNK = "endChunk";
|
||||
public static final String NAME = "period";
|
||||
private final PlatformEventType eventType;
|
||||
private String value = EVERY_CHUNK;
|
||||
|
||||
public PeriodSetting(PlatformEventType eventType, String defaultValue) {
|
||||
super(defaultValue);
|
||||
this.eventType = Objects.requireNonNull(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Set<String> values) {
|
||||
|
||||
boolean beginChunk = false;
|
||||
boolean endChunk = false;
|
||||
Long min = null;
|
||||
String text = null;
|
||||
for (String value : values) {
|
||||
switch (value) {
|
||||
case EVERY_CHUNK:
|
||||
beginChunk = true;
|
||||
endChunk = true;
|
||||
break;
|
||||
case BEGIN_CHUNK:
|
||||
beginChunk = true;
|
||||
break;
|
||||
case END_CHUNK:
|
||||
endChunk = true;
|
||||
break;
|
||||
default:
|
||||
long l = Utils.parseTimespanWithInfinity(value);
|
||||
// Always accept first specified value
|
||||
if (min == null) {
|
||||
text = value;
|
||||
min = l;
|
||||
} else {
|
||||
if (l < min) {
|
||||
text = value;
|
||||
min = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// A specified interval trumps *_CHUNK
|
||||
if (min != null) {
|
||||
return text;
|
||||
}
|
||||
if (beginChunk && !endChunk) {
|
||||
return BEGIN_CHUNK;
|
||||
}
|
||||
if (!beginChunk && endChunk) {
|
||||
return END_CHUNK;
|
||||
}
|
||||
return EVERY_CHUNK; // also default
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
switch (value) {
|
||||
case EVERY_CHUNK:
|
||||
eventType.setPeriod(0, true, true);
|
||||
break;
|
||||
case BEGIN_CHUNK:
|
||||
eventType.setPeriod(0, true, false);
|
||||
break;
|
||||
case END_CHUNK:
|
||||
eventType.setPeriod(0, false, true);
|
||||
break;
|
||||
default:
|
||||
long nanos = Utils.parseTimespanWithInfinity(value);
|
||||
if (nanos != Long.MAX_VALUE) {
|
||||
eventType.setPeriod(nanos / 1_000_000, false, false);
|
||||
} else {
|
||||
eventType.setPeriod(Long.MAX_VALUE, false, false);
|
||||
}
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean isType(long typeId) {
|
||||
return PeriodSetting.typeId == typeId;
|
||||
}
|
||||
}
|
||||
75
jdkSrc/jdk8/jdk/jfr/internal/settings/StackTraceSetting.java
Normal file
75
jdkSrc/jdk8/jdk/jfr/internal/settings/StackTraceSetting.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.BooleanFlag;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Control;
|
||||
import jdk.jfr.internal.Type;
|
||||
|
||||
@MetadataDefinition
|
||||
@Label("Stack Trace")
|
||||
@Name(Type.SETTINGS_PREFIX + "StackTrace")
|
||||
@Description("Record stack traces")
|
||||
@BooleanFlag
|
||||
public final class StackTraceSetting extends Control {
|
||||
private final static long typeId = Type.getTypeId(StackTraceSetting.class);
|
||||
private final BooleanValue booleanValue;
|
||||
private final PlatformEventType eventType;
|
||||
|
||||
public StackTraceSetting(PlatformEventType eventType, String defaultValue) {
|
||||
super(defaultValue);
|
||||
this.booleanValue = BooleanValue.valueOf(defaultValue);
|
||||
this.eventType = Objects.requireNonNull(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Set<String> values) {
|
||||
return booleanValue.union(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
booleanValue.setValue(value);
|
||||
eventType.setStackTraceEnabled(booleanValue.getBoolean());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return booleanValue.getValue();
|
||||
}
|
||||
|
||||
public static boolean isType(long typeId) {
|
||||
return StackTraceSetting.typeId == typeId;
|
||||
}
|
||||
}
|
||||
90
jdkSrc/jdk8/jdk/jfr/internal/settings/ThresholdSetting.java
Normal file
90
jdkSrc/jdk8/jdk/jfr/internal/settings/ThresholdSetting.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Control;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.Utils;
|
||||
@MetadataDefinition
|
||||
@Label("Threshold")
|
||||
@Name(Type.SETTINGS_PREFIX + "Threshold")
|
||||
@Description("Record event with duration above or equal to threshold")
|
||||
@Timespan
|
||||
public final class ThresholdSetting extends Control {
|
||||
private final static long typeId = Type.getTypeId(ThresholdSetting.class);
|
||||
private String value = "0 ns";
|
||||
private final PlatformEventType eventType;
|
||||
|
||||
public ThresholdSetting(PlatformEventType eventType, String defaultValue) {
|
||||
super(defaultValue);
|
||||
this.eventType = Objects.requireNonNull(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Set<String> values) {
|
||||
Long min = null;
|
||||
String text = null;
|
||||
for (String value : values) {
|
||||
long l = Utils.parseTimespanWithInfinity(value);
|
||||
// always accept first value
|
||||
if (min == null) {
|
||||
min = l;
|
||||
text = value;
|
||||
} else {
|
||||
if (l < min) {
|
||||
text = value;
|
||||
min = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
return text == null ? "0 ns" : text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
long l = Utils.parseTimespanWithInfinity(value);
|
||||
this.value = value;
|
||||
eventType.setThreshold(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean isType(long typeId) {
|
||||
return ThresholdSetting.typeId == typeId;
|
||||
}
|
||||
}
|
||||
48
jdkSrc/jdk8/jdk/jfr/internal/test/WhiteBox.java
Normal file
48
jdkSrc/jdk8/jdk/jfr/internal/test/WhiteBox.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 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.test;
|
||||
|
||||
public final class WhiteBox {
|
||||
|
||||
private static boolean writeAllObjectSamples;
|
||||
|
||||
/**
|
||||
* If OldObjectSample event is enabled, calling this method
|
||||
* ensures that all object samples are written, including short-lived objects.
|
||||
* Purpose of this method is to increase determinism in tests.
|
||||
*
|
||||
* @param writeAllObjectSamples if all samples should be written or not
|
||||
*
|
||||
*/
|
||||
public static void setWriteAllObjectSamples(boolean writeAllSamples) {
|
||||
writeAllObjectSamples = writeAllSamples;
|
||||
}
|
||||
|
||||
public static boolean getWriteAllObjectSamples() {
|
||||
return writeAllObjectSamples;
|
||||
}
|
||||
|
||||
}
|
||||
127
jdkSrc/jdk8/jdk/jfr/internal/tool/Assemble.java
Normal file
127
jdkSrc/jdk8/jdk/jfr/internal/tool/Assemble.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
final class Assemble extends Command {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "assemble";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
return Collections.singletonList("<repository> <file>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Assemble leftover chunks from a disk repository into a recording file";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayOptionUsage(PrintStream stream) {
|
||||
stream.println(" <repository> Directory where the repository is located");
|
||||
stream.println();
|
||||
stream.println(" <file> Name of the recording file (.jfr) to create");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
ensureMinArgumentCount(options, 2);
|
||||
ensureMaxArgumentCount(options, 2);
|
||||
Path repository = getDirectory(options.pop());
|
||||
|
||||
Path file = Paths.get(options.pop());
|
||||
ensureFileDoesNotExist(file);
|
||||
ensureJFRFile(file);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file.toFile())) {
|
||||
List<Path> files = listJFRFiles(repository);
|
||||
if (files.isEmpty()) {
|
||||
throw new UserDataException("no *.jfr files found at " + repository);
|
||||
}
|
||||
println();
|
||||
println("Assembling files... ");
|
||||
println();
|
||||
transferTo(files, file, fos.getChannel());
|
||||
println();
|
||||
println("Finished.");
|
||||
} catch (IOException e) {
|
||||
throw new UserDataException("could not open destination file " + file + ". " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Path> listJFRFiles(Path path) throws UserDataException {
|
||||
try {
|
||||
List<Path> files = new ArrayList<>();
|
||||
if (Files.isDirectory(path)) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, "*.jfr")) {
|
||||
for (Path p : stream) {
|
||||
if (!Files.isDirectory(p) && Files.isReadable(p)) {
|
||||
files.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
files.sort((u, v) -> u.getFileName().compareTo(v.getFileName()));
|
||||
return files;
|
||||
} catch (IOException ioe) {
|
||||
throw new UserDataException("could not list *.jfr for directory " + path + ". " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void transferTo(List<Path> sourceFiles, Path output, FileChannel out) throws UserDataException {
|
||||
long pos = 0;
|
||||
for (Path p : sourceFiles) {
|
||||
println(" " + p.toString());
|
||||
try (FileChannel sourceChannel = FileChannel.open(p)) {
|
||||
long rem = Files.size(p);
|
||||
while (rem > 0) {
|
||||
long n = Math.min(rem, 1024 * 1024);
|
||||
long w = out.transferFrom(sourceChannel, pos, n);
|
||||
pos += w;
|
||||
rem -= w;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new UserDataException("could not copy recording chunk " + p + " to new file. " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
306
jdkSrc/jdk8/jdk/jfr/internal/tool/Command.java
Normal file
306
jdkSrc/jdk8/jdk/jfr/internal/tool/Command.java
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.internal.tool;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
abstract class Command {
|
||||
public final static String title = "Tool for working with Flight Recorder files (.jfr)";
|
||||
private final static Command HELP = new Help();
|
||||
private final static List<Command> COMMANDS = createCommands();
|
||||
|
||||
private static List<Command> createCommands() {
|
||||
List<Command> commands = new ArrayList<>();
|
||||
commands.add(new Print());
|
||||
commands.add(new Metadata());
|
||||
commands.add(new Summary());
|
||||
commands.add(new Assemble());
|
||||
commands.add(new Disassemble());
|
||||
commands.add(new Version());
|
||||
commands.add(HELP);
|
||||
return Collections.unmodifiableList(commands);
|
||||
}
|
||||
|
||||
static void displayHelp() {
|
||||
System.out.println(title);
|
||||
System.out.println();
|
||||
displayAvailableCommands(System.out);
|
||||
}
|
||||
|
||||
abstract public String getName();
|
||||
|
||||
abstract public String getDescription();
|
||||
|
||||
abstract public void execute(Deque<String> argList) throws UserSyntaxException, UserDataException;
|
||||
|
||||
protected String getTitle() {
|
||||
return getDescription();
|
||||
}
|
||||
|
||||
static void displayAvailableCommands(PrintStream stream) {
|
||||
boolean first = true;
|
||||
for (Command c : Command.COMMANDS) {
|
||||
if (!first) {
|
||||
System.out.println();
|
||||
}
|
||||
displayCommand(stream, c);
|
||||
stream.println(" " + c.getDescription());
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void displayCommand(PrintStream stream, Command c) {
|
||||
boolean firstSyntax = true;
|
||||
String alias = buildAlias(c);
|
||||
String initial = " jfr " + c.getName();
|
||||
for (String syntaxLine : c.getOptionSyntax()) {
|
||||
if (firstSyntax) {
|
||||
if (syntaxLine.length() != 0) {
|
||||
stream.println(initial + " " + syntaxLine + alias);
|
||||
} else {
|
||||
stream.println(initial + alias);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < initial.length(); i++) {
|
||||
stream.print(" ");
|
||||
}
|
||||
stream.println(" " + syntaxLine);
|
||||
}
|
||||
firstSyntax = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildAlias(Command c) {
|
||||
List<String> aliases = c.getAliases();
|
||||
if (aliases.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (aliases.size() == 1) {
|
||||
sb.append(" (alias ");
|
||||
sb.append(aliases.get(0));
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(" (aliases ");
|
||||
for (int i = 0; i< aliases.size(); i ++ ) {
|
||||
sb.append(aliases.get(i));
|
||||
if (i < aliases.size() -1) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static List<Command> getCommands() {
|
||||
return COMMANDS;
|
||||
}
|
||||
|
||||
public static Command valueOf(String commandName) {
|
||||
for (Command command : COMMANDS) {
|
||||
if (command.getName().equals(commandName)) {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> getOptionSyntax() {
|
||||
return Collections.singletonList("");
|
||||
}
|
||||
|
||||
public void displayOptionUsage(PrintStream stream) {
|
||||
}
|
||||
|
||||
protected boolean acceptOption(Deque<String> options, String expected) throws UserSyntaxException {
|
||||
if (expected.equals(options.peek())) {
|
||||
if (options.size() < 2) {
|
||||
throw new UserSyntaxException("missing value for " + options.peek());
|
||||
}
|
||||
options.remove();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void warnForWildcardExpansion(String option, String filter) throws UserDataException {
|
||||
// Users should quote their wildcards to avoid expansion by the shell
|
||||
try {
|
||||
if (!filter.contains(File.pathSeparator)) {
|
||||
Path p = Paths.get(".", filter);
|
||||
if (!Files.exists(p)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new UserDataException("wildcards should be quoted, for example " + option + " \"Foo*\"");
|
||||
} catch (InvalidPathException ipe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean acceptFilterOption(Deque<String> options, String expected) throws UserSyntaxException {
|
||||
if (!acceptOption(options, expected)) {
|
||||
return false;
|
||||
}
|
||||
if (options.isEmpty()) {
|
||||
throw new UserSyntaxException("missing filter after " + expected);
|
||||
}
|
||||
String filter = options.peek();
|
||||
if (filter.startsWith("--")) {
|
||||
throw new UserSyntaxException("missing filter after " + expected);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
final protected void ensureMaxArgumentCount(Deque<String> options, int maxCount) throws UserSyntaxException {
|
||||
if (options.size() > maxCount) {
|
||||
throw new UserSyntaxException("too many arguments");
|
||||
}
|
||||
}
|
||||
|
||||
final protected void ensureMinArgumentCount(Deque<String> options, int minCount) throws UserSyntaxException {
|
||||
if (options.size() < minCount) {
|
||||
throw new UserSyntaxException("too few arguments");
|
||||
}
|
||||
}
|
||||
|
||||
final protected Path getDirectory(String pathText) throws UserDataException {
|
||||
try {
|
||||
Path path = Paths.get(pathText).toAbsolutePath();
|
||||
if (!Files.exists((path))) {
|
||||
throw new UserDataException("directory does not exist, " + pathText);
|
||||
}
|
||||
if (!Files.isDirectory(path)) {
|
||||
throw new UserDataException("path must be directory, " + pathText);
|
||||
}
|
||||
return path;
|
||||
} catch (InvalidPathException ipe) {
|
||||
throw new UserDataException("invalid path '" + pathText + "'");
|
||||
}
|
||||
}
|
||||
|
||||
final protected Path getJFRInputFile(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
if (options.isEmpty()) {
|
||||
throw new UserSyntaxException("missing file");
|
||||
}
|
||||
String file = options.removeLast();
|
||||
if (file.startsWith("--")) {
|
||||
throw new UserSyntaxException("missing file");
|
||||
}
|
||||
try {
|
||||
Path path = Paths.get(file).toAbsolutePath();
|
||||
ensureAccess(path);
|
||||
ensureJFRFile(path);
|
||||
return path;
|
||||
} catch (IOError ioe) {
|
||||
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
|
||||
} catch (InvalidPathException ipe) {
|
||||
throw new UserDataException("invalid path '" + file + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureAccess(Path path) throws UserDataException {
|
||||
try (RandomAccessFile rad = new RandomAccessFile(path.toFile(), "r")) {
|
||||
if (rad.length() == 0) {
|
||||
throw new UserDataException("file is empty '" + path + "'");
|
||||
}
|
||||
rad.read(); // try to read 1 byte
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new UserDataException("could not open file " + e.getMessage());
|
||||
} catch (IOException e) {
|
||||
throw new UserDataException("i/o error reading file '" + path + "', " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
final protected void couldNotReadError(Path p, IOException e) throws UserDataException {
|
||||
throw new UserDataException("could not read recording at " + p.toAbsolutePath() + ". " + e.getMessage());
|
||||
}
|
||||
|
||||
final protected Path ensureFileDoesNotExist(Path file) throws UserDataException {
|
||||
if (Files.exists(file)) {
|
||||
throw new UserDataException("file '" + file + "' already exists");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
final protected void ensureJFRFile(Path path) throws UserDataException {
|
||||
if (!path.toString().endsWith(".jfr")) {
|
||||
throw new UserDataException("filename must end with '.jfr'");
|
||||
}
|
||||
}
|
||||
|
||||
protected void displayUsage(PrintStream stream) {
|
||||
displayCommand(stream, this);
|
||||
stream.println();
|
||||
displayOptionUsage(stream);
|
||||
}
|
||||
|
||||
final protected void println() {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
final protected void print(String text) {
|
||||
System.out.print(text);
|
||||
}
|
||||
|
||||
final protected void println(String text) {
|
||||
System.out.println(text);
|
||||
}
|
||||
|
||||
final protected boolean matches(String command) {
|
||||
for (String s : getNames()) {
|
||||
if (s.equals(command)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List<String> getAliases() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<String> getNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add(getName());
|
||||
names.addAll(getAliases());
|
||||
return names;
|
||||
}
|
||||
}
|
||||
250
jdkSrc/jdk8/jdk/jfr/internal/tool/Disassemble.java
Normal file
250
jdkSrc/jdk8/jdk/jfr/internal/tool/Disassemble.java
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
|
||||
final class Disassemble extends Command {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "disassemble";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("[--output <directory>]");
|
||||
list.add("[--max-chunks <chunks>]");
|
||||
list.add("[--max-size <size>]");
|
||||
list.add("<file>");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayOptionUsage(PrintStream stream) {
|
||||
stream.println(" --output <directory> The location to write the disassembled file,");
|
||||
stream.println(" by default the current directory");
|
||||
stream.println("");
|
||||
stream.println(" --max-chunks <chunks> Maximum number of chunks per disassembled file,");
|
||||
stream.println(" by default 5. The chunk size varies, but is ");
|
||||
stream.println(" typically around 15 MB.");
|
||||
stream.println("");
|
||||
stream.println(" --max-size <size> Maximum number of bytes per file.");
|
||||
stream.println("");
|
||||
stream.println(" <file> Location of the recording file (.jfr)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Disassamble a recording file into smaller files/chunks";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
if (options.isEmpty()) {
|
||||
throw new UserSyntaxException("missing file");
|
||||
}
|
||||
Path file = getJFRInputFile(options);
|
||||
int maxChunks = Integer.MAX_VALUE;
|
||||
int maxsize = Integer.MAX_VALUE;
|
||||
String output = System.getProperty("user.dir");
|
||||
int optionCount = options.size();
|
||||
while (optionCount > 0) {
|
||||
if (acceptOption(options, "--output")) {
|
||||
output = options.pop();
|
||||
}
|
||||
if (acceptOption(options, "--max-size")) {
|
||||
String value = options.pop();
|
||||
try {
|
||||
maxsize = Integer.parseInt(value);
|
||||
if (maxsize < 1) {
|
||||
throw new UserDataException("max size must be at least 1");
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new UserDataException("not a valid value for --max-size.");
|
||||
}
|
||||
}
|
||||
if (acceptOption(options, "--max-chunks")) {
|
||||
String value = options.pop();
|
||||
try {
|
||||
maxChunks = Integer.parseInt(value);
|
||||
if (maxChunks < 1) {
|
||||
throw new UserDataException("max chunks must be at least 1.");
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new UserDataException("not a valid value for --max-size.");
|
||||
}
|
||||
}
|
||||
if (optionCount == options.size()) {
|
||||
// No progress made
|
||||
throw new UserSyntaxException("unknown option " + options.peek());
|
||||
}
|
||||
optionCount = options.size();
|
||||
}
|
||||
Path outputPath = getDirectory(output);
|
||||
|
||||
println();
|
||||
println("Examining recording " + file + " ...");
|
||||
List<Long> sizes;
|
||||
if (maxsize != Integer.MAX_VALUE && maxChunks == Integer.MAX_VALUE) {
|
||||
try {
|
||||
long fileSize = Files.size(file);
|
||||
if (maxsize >=fileSize) {
|
||||
println();
|
||||
println("File size (" + fileSize +") does not exceed max size (" + maxsize + ")");
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UserDataException("unexpected i/o error when determining file size" + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (maxsize == Integer.MAX_VALUE && maxChunks == Integer.MAX_VALUE) {
|
||||
maxChunks = 5;
|
||||
}
|
||||
|
||||
try {
|
||||
sizes = findChunkSizes(file);
|
||||
} catch (IOException e) {
|
||||
throw new UserDataException("unexpected i/o error. " + e.getMessage());
|
||||
}
|
||||
if (maxsize == Integer.MAX_VALUE == sizes.size() <= maxChunks) {
|
||||
throw new UserDataException("number of chunks in recording (" + sizes.size() + ") doesn't exceed max chunks (" + maxChunks + ")");
|
||||
}
|
||||
println();
|
||||
if (sizes.size() > 0) {
|
||||
List<Long> combinedSizes = combineChunkSizes(sizes, maxChunks, maxsize);
|
||||
print("File consists of " + sizes.size() + " chunks. The recording will be split into ");
|
||||
println(combinedSizes.size() + " files");
|
||||
println();
|
||||
splitFile(outputPath, file, combinedSizes);
|
||||
} else {
|
||||
throw new UserDataException("no JFR chunks found in file.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> findChunkSizes(Path p) throws IOException {
|
||||
try (RecordingInput input = new RecordingInput(p.toFile())) {
|
||||
List<Long> sizes = new ArrayList<>();
|
||||
ChunkHeader ch = new ChunkHeader(input);
|
||||
sizes.add(ch.getSize());
|
||||
while (!ch.isLastChunk()) {
|
||||
ch = ch.nextHeader();
|
||||
sizes.add(ch.getSize());
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> combineChunkSizes(List<Long> sizes, int maxChunks, long maxSize) {
|
||||
List<Long> reduced = new ArrayList<Long>();
|
||||
int chunks = 1;
|
||||
long fileSize = sizes.get(0);
|
||||
for (int i = 1; i < sizes.size(); i++) {
|
||||
long size = sizes.get(i);
|
||||
if (fileSize + size > maxSize) {
|
||||
reduced.add(fileSize);
|
||||
chunks = 1;
|
||||
fileSize = size;
|
||||
continue;
|
||||
}
|
||||
fileSize += size;
|
||||
if (chunks == maxChunks) {
|
||||
reduced.add(fileSize);
|
||||
fileSize = 0;
|
||||
chunks = 1;
|
||||
continue;
|
||||
}
|
||||
chunks++;
|
||||
}
|
||||
if (fileSize != 0) {
|
||||
reduced.add(fileSize);
|
||||
}
|
||||
return reduced;
|
||||
}
|
||||
|
||||
private void splitFile(Path directory, Path file, List<Long> splitPositions) throws UserDataException {
|
||||
int padAmountZeros = String.valueOf(splitPositions.size() - 1).length();
|
||||
String fileName = file.getFileName().toString();
|
||||
String fileFormatter = fileName.subSequence(0, fileName.length() - 4) + "_%0" + padAmountZeros + "d.jfr";
|
||||
for (int i = 0; i < splitPositions.size(); i++) {
|
||||
String formattedFilename = String.format(fileFormatter, i);
|
||||
try {
|
||||
Path p = directory.resolve(formattedFilename);
|
||||
if (Files.exists(p)) {
|
||||
throw new UserDataException("can't create disassembled file " + p + ", a file with that name already exist");
|
||||
}
|
||||
} catch (InvalidPathException ipe) {
|
||||
throw new UserDataException("can't construct path with filename" + formattedFilename);
|
||||
}
|
||||
}
|
||||
|
||||
try (DataInputStream stream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.toFile())))) {
|
||||
for (int i = 0; i < splitPositions.size(); i++) {
|
||||
Long l = splitPositions.get(i);
|
||||
byte[] bytes = readBytes(stream, l.intValue());
|
||||
String formattedFilename = String.format(fileFormatter, i);
|
||||
Path p = directory.resolve(formattedFilename);
|
||||
File splittedFile = p.toFile();
|
||||
println("Writing " + splittedFile + " ... " + bytes.length);
|
||||
FileOutputStream fos = new FileOutputStream(splittedFile);
|
||||
fos.write(bytes);
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new UserDataException("i/o error writing file " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] readBytes(InputStream stream, int count) throws UserDataException, IOException {
|
||||
byte[] data = new byte[count];
|
||||
int totalRead = 0;
|
||||
while (totalRead < data.length) {
|
||||
int read = stream.read(data, totalRead, data.length - totalRead);
|
||||
if (read == -1) {
|
||||
throw new UserDataException("unexpected end of data");
|
||||
}
|
||||
totalRead += read;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
139
jdkSrc/jdk8/jdk/jfr/internal/tool/EventPrintWriter.java
Normal file
139
jdkSrc/jdk8/jdk/jfr/internal/tool/EventPrintWriter.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 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.tool;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.Timestamp;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedObject;
|
||||
import jdk.jfr.consumer.RecordingFile;
|
||||
import jdk.jfr.internal.consumer.RecordingInternals;
|
||||
|
||||
abstract class EventPrintWriter extends StructuredWriter {
|
||||
|
||||
enum ValueType {
|
||||
TIMESPAN, TIMESTAMP, OTHER
|
||||
}
|
||||
|
||||
protected static final String STACK_TRACE_FIELD = "stackTrace";
|
||||
protected static final String EVENT_THREAD_FIELD = "eventThread";
|
||||
|
||||
private Predicate<EventType> eventFilter = x -> true;
|
||||
private int stackDepth;
|
||||
|
||||
// cach that will speed up annotation lookup
|
||||
private Map<ValueDescriptor, ValueType> typeOfValues = new HashMap<>();
|
||||
|
||||
EventPrintWriter(PrintWriter p) {
|
||||
super(p);
|
||||
}
|
||||
|
||||
abstract protected void print(List<RecordedEvent> events);
|
||||
|
||||
void print(Path source) throws FileNotFoundException, IOException {
|
||||
List<RecordedEvent> events = new ArrayList<>(500_000);
|
||||
printBegin();
|
||||
try (RecordingFile file = new RecordingFile(source)) {
|
||||
while (file.hasMoreEvents()) {
|
||||
RecordedEvent event = file.readEvent();
|
||||
if (acceptEvent(event)) {
|
||||
events.add(event);
|
||||
}
|
||||
if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) {
|
||||
RecordingInternals.INSTANCE.sort(events);
|
||||
print(events);
|
||||
events.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
printEnd();
|
||||
flush(true);
|
||||
}
|
||||
|
||||
protected void printEnd() {
|
||||
}
|
||||
|
||||
protected void printBegin() {
|
||||
}
|
||||
|
||||
public final void setEventFilter(Predicate<EventType> eventFilter) {
|
||||
this.eventFilter = eventFilter;
|
||||
}
|
||||
|
||||
protected final boolean acceptEvent(RecordedEvent event) {
|
||||
return eventFilter.test(event.getEventType());
|
||||
}
|
||||
|
||||
protected final int getStackDepth() {
|
||||
return stackDepth;
|
||||
}
|
||||
|
||||
protected final boolean isLateField(String name) {
|
||||
return name.equals(EVENT_THREAD_FIELD) || name.equals(STACK_TRACE_FIELD);
|
||||
}
|
||||
|
||||
public void setStackDepth(int stackDepth) {
|
||||
this.stackDepth = stackDepth;
|
||||
}
|
||||
|
||||
protected Object getValue(RecordedObject object, ValueDescriptor v) {
|
||||
ValueType valueType = typeOfValues.get(v);
|
||||
if (valueType == null) {
|
||||
valueType = determineValueType(v);
|
||||
typeOfValues.put(v, valueType);
|
||||
}
|
||||
switch (valueType) {
|
||||
case TIMESPAN:
|
||||
return object.getDuration(v.getName());
|
||||
case TIMESTAMP:
|
||||
return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName());
|
||||
default:
|
||||
return object.getValue(v.getName());
|
||||
}
|
||||
}
|
||||
// It's expensive t check
|
||||
private ValueType determineValueType(ValueDescriptor v) {
|
||||
if (v.getAnnotation(Timespan.class) != null) {
|
||||
return ValueType.TIMESPAN;
|
||||
}
|
||||
if (v.getAnnotation(Timestamp.class) != null) {
|
||||
return ValueType.TIMESTAMP;
|
||||
}
|
||||
return ValueType.OTHER;
|
||||
}
|
||||
}
|
||||
76
jdkSrc/jdk8/jdk/jfr/internal/tool/Help.java
Normal file
76
jdkSrc/jdk8/jdk/jfr/internal/tool/Help.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
final class Help extends Command {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "help";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
return Collections.singletonList("[<command>]");
|
||||
}
|
||||
|
||||
protected List<String> getAliases() {
|
||||
return Arrays.asList("--help", "-h", "-?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayOptionUsage(PrintStream stream) {
|
||||
println(" <command> The name of the command to get help for");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Display all available commands, or help about a specific command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
if (options.isEmpty()) {
|
||||
Command.displayHelp();
|
||||
return;
|
||||
}
|
||||
ensureMaxArgumentCount(options, 1);
|
||||
String commandName = options.remove();
|
||||
Command c = Command.valueOf(commandName);
|
||||
if (c == null) {
|
||||
throw new UserDataException("unknown command '" + commandName + "'");
|
||||
}
|
||||
println(c.getTitle());
|
||||
println();
|
||||
c.displayUsage(System.out);
|
||||
}
|
||||
}
|
||||
261
jdkSrc/jdk8/jdk/jfr/internal/tool/JSONWriter.java
Normal file
261
jdkSrc/jdk8/jdk/jfr/internal/tool/JSONWriter.java
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedFrame;
|
||||
import jdk.jfr.consumer.RecordedObject;
|
||||
|
||||
final class JSONWriter extends EventPrintWriter {
|
||||
|
||||
private boolean first = true;
|
||||
|
||||
public JSONWriter(PrintWriter writer) {
|
||||
super(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printBegin() {
|
||||
printObjectBegin();
|
||||
printDataStructureName("recording");
|
||||
printObjectBegin();
|
||||
printDataStructureName("events");
|
||||
printArrayBegin();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void print(List<RecordedEvent> events) {
|
||||
for (RecordedEvent event : events) {
|
||||
printNewDataStructure(first, true, null);
|
||||
printEvent(event);
|
||||
flush(false);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printEnd() {
|
||||
printArrayEnd();;
|
||||
printObjectEnd();
|
||||
printObjectEnd();
|
||||
}
|
||||
|
||||
private void printEvent(RecordedEvent event) {
|
||||
printObjectBegin();
|
||||
EventType type = event.getEventType();
|
||||
printValue(true, false, "type", type.getName());
|
||||
printNewDataStructure(false, false, "values");
|
||||
printObjectBegin();
|
||||
boolean first = true;
|
||||
for (ValueDescriptor v : event.getFields()) {
|
||||
printValueDescriptor(first, false, v, getValue(event, v));
|
||||
first = false;
|
||||
}
|
||||
printObjectEnd();
|
||||
printObjectEnd();
|
||||
}
|
||||
|
||||
void printValue(boolean first, boolean arrayElement, String name, Object value) {
|
||||
printNewDataStructure(first, arrayElement, name);
|
||||
if (!printIfNull(value)) {
|
||||
if (value instanceof Boolean) {
|
||||
printAsString(value);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
Double dValue = (Double) value;
|
||||
if (Double.isNaN(dValue) || Double.isInfinite(dValue)) {
|
||||
printNull();
|
||||
return;
|
||||
}
|
||||
printAsString(value);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Float) {
|
||||
Float fValue = (Float) value;
|
||||
if (Float.isNaN(fValue) || Float.isInfinite(fValue)) {
|
||||
printNull();
|
||||
return;
|
||||
}
|
||||
printAsString(value);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
printAsString(value);
|
||||
return;
|
||||
}
|
||||
print("\"");
|
||||
printEscaped(String.valueOf(value));
|
||||
print("\"");
|
||||
}
|
||||
}
|
||||
|
||||
public void printObject(RecordedObject object) {
|
||||
printObjectBegin();
|
||||
boolean first = true;
|
||||
for (ValueDescriptor v : object.getFields()) {
|
||||
printValueDescriptor(first, false, v, getValue(object, v));
|
||||
first = false;
|
||||
}
|
||||
printObjectEnd();
|
||||
}
|
||||
|
||||
private void printArray(ValueDescriptor v, Object[] array) {
|
||||
printArrayBegin();
|
||||
boolean first = true;
|
||||
int depth = 0;
|
||||
for (Object arrayElement : array) {
|
||||
if (!(arrayElement instanceof RecordedFrame) || depth < getStackDepth()) {
|
||||
printValueDescriptor(first, true, v, arrayElement);
|
||||
}
|
||||
depth++;
|
||||
first = false;
|
||||
}
|
||||
printArrayEnd();
|
||||
}
|
||||
|
||||
private void printValueDescriptor(boolean first, boolean arrayElement, ValueDescriptor vd, Object value) {
|
||||
if (vd.isArray() && !arrayElement) {
|
||||
printNewDataStructure(first, arrayElement, vd.getName());
|
||||
if (!printIfNull(value)) {
|
||||
printArray(vd, (Object[]) value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!vd.getFields().isEmpty()) {
|
||||
printNewDataStructure(first, arrayElement, vd.getName());
|
||||
if (!printIfNull(value)) {
|
||||
printObject((RecordedObject) value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
printValue(first, arrayElement, vd.getName(), value);
|
||||
}
|
||||
|
||||
private void printNewDataStructure(boolean first, boolean arrayElement, String name) {
|
||||
if (!first) {
|
||||
print(", ");
|
||||
if (!arrayElement) {
|
||||
println();
|
||||
}
|
||||
}
|
||||
if (!arrayElement) {
|
||||
printDataStructureName(name);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean printIfNull(Object value) {
|
||||
if (value == null) {
|
||||
printNull();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void printNull() {
|
||||
print("null");
|
||||
}
|
||||
|
||||
private void printDataStructureName(String text) {
|
||||
printIndent();
|
||||
print("\"");
|
||||
printEscaped(text);
|
||||
print("\": ");
|
||||
}
|
||||
|
||||
private void printObjectEnd() {
|
||||
retract();
|
||||
println();
|
||||
printIndent();
|
||||
print("}");
|
||||
}
|
||||
|
||||
private void printObjectBegin() {
|
||||
println("{");
|
||||
indent();
|
||||
}
|
||||
|
||||
private void printArrayEnd() {
|
||||
print("]");
|
||||
}
|
||||
|
||||
private void printArrayBegin() {
|
||||
print("[");
|
||||
}
|
||||
|
||||
private void printEscaped(String text) {
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
printEscaped(text.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void printEscaped(char c) {
|
||||
if (c == '\b') {
|
||||
print("\\b");
|
||||
return;
|
||||
}
|
||||
if (c == '\n') {
|
||||
print("\\n");
|
||||
return;
|
||||
}
|
||||
if (c == '\t') {
|
||||
print("\\t");
|
||||
return;
|
||||
}
|
||||
if (c == '\f') {
|
||||
print("\\f");
|
||||
return;
|
||||
}
|
||||
if (c == '\r') {
|
||||
print("\\r");
|
||||
return;
|
||||
}
|
||||
if (c == '\"') {
|
||||
print("\\\"");
|
||||
return;
|
||||
}
|
||||
if (c == '\\') {
|
||||
print("\\\\");
|
||||
return;
|
||||
}
|
||||
if (c == '/') {
|
||||
print("\\/");
|
||||
return;
|
||||
}
|
||||
if (c > 0x7F || c < 32) {
|
||||
print("\\u");
|
||||
// 0x10000 will pad with zeros.
|
||||
print(Integer.toHexString(0x10000 + (int) c).substring(1));
|
||||
return;
|
||||
}
|
||||
print(c);
|
||||
}
|
||||
}
|
||||
110
jdkSrc/jdk8/jdk/jfr/internal/tool/Main.java
Normal file
110
jdkSrc/jdk8/jdk/jfr/internal/tool/Main.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Launcher class for the JDK_HOME\bin\jfr tool
|
||||
*
|
||||
*/
|
||||
public final class Main {
|
||||
|
||||
private static final int EXIT_OK = 0;
|
||||
private static final int EXIT_FAILED = 1;
|
||||
private static final int EXIT_WRONG_ARGUMENTS = 2;
|
||||
|
||||
public static void main(String... args) {
|
||||
Deque<String> argList = new LinkedList<>(Arrays.asList(args));
|
||||
if (argList.isEmpty()) {
|
||||
System.out.println(Command.title);
|
||||
System.out.println();
|
||||
System.out.println("Before using this tool, you must have a recording file.");
|
||||
System.out.println("A file can be created by starting a recording from command line:");
|
||||
System.out.println();
|
||||
System.out.println(" java -XX:StartFlightRecording:filename=recording.jfr,duration=30s ... ");
|
||||
System.out.println();
|
||||
System.out.println("A recording can also be started on already running Java Virtual Machine:");
|
||||
System.out.println();
|
||||
System.out.println(" jcmd (to list available pids)");
|
||||
System.out.println(" jcmd <pid> JFR.start");
|
||||
System.out.println();
|
||||
System.out.println("Recording data can be dumped to file using the JFR.dump command:");
|
||||
System.out.println();
|
||||
System.out.println(" jcmd <pid> JFR.dump filename=recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println("The contents of the recording can then be printed, for example:");
|
||||
System.out.println();
|
||||
System.out.println(" jfr print recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println(" jfr print --events CPULoad,GarbageCollection recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println(" jfr print --json --events CPULoad recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println(" jfr print --categories \"GC,JVM,Java*\" recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println(" jfr print --events \"jdk.*\" --stack-depth 64 recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println(" jfr summary recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println(" jfr metadata recording.jfr");
|
||||
System.out.println();
|
||||
System.out.println("For more information about available commands, use 'jfr help'");
|
||||
System.exit(EXIT_OK);
|
||||
}
|
||||
String command = argList.remove();
|
||||
for (Command c : Command.getCommands()) {
|
||||
if (c.matches(command)) {
|
||||
try {
|
||||
c.execute(argList);
|
||||
System.exit(EXIT_OK);
|
||||
} catch (UserDataException ude) {
|
||||
System.err.println("jfr " + c.getName() + ": " + ude.getMessage());
|
||||
System.exit(EXIT_FAILED);
|
||||
} catch (UserSyntaxException use) {
|
||||
System.err.println("jfr " + c.getName() + ": " + use.getMessage());
|
||||
System.err.println();
|
||||
System.err.println("Usage:");
|
||||
System.err.println();
|
||||
c.displayUsage(System.err);
|
||||
System.exit(EXIT_WRONG_ARGUMENTS);
|
||||
} catch (Throwable e) {
|
||||
System.err.println("jfr " + c.getName() + ": unexpected internal error, " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.exit(EXIT_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.err.println("jfr: unknown command '" + command + "'");
|
||||
System.err.println();
|
||||
System.err.println("List of available commands:");
|
||||
System.err.println();
|
||||
Command.displayAvailableCommands(System.err);
|
||||
System.exit(EXIT_WRONG_ARGUMENTS);
|
||||
}
|
||||
}
|
||||
139
jdkSrc/jdk8/jdk/jfr/internal/tool/Metadata.java
Normal file
139
jdkSrc/jdk8/jdk/jfr/internal/tool/Metadata.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 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.tool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.consumer.RecordingFile;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.consumer.RecordingInternals;
|
||||
|
||||
final class Metadata extends Command {
|
||||
|
||||
private static class TypeComparator implements Comparator<Type> {
|
||||
|
||||
@Override
|
||||
public int compare(Type t1, Type t2) {
|
||||
int g1 = groupValue(t1);
|
||||
int g2 = groupValue(t2);
|
||||
if (g1 == g2) {
|
||||
String n1 = t1.getName();
|
||||
String n2 = t2.getName();
|
||||
String package1 = n1.substring(0, n1.lastIndexOf('.') + 1);
|
||||
String package2 = n2.substring(0, n2.lastIndexOf('.') + 1);
|
||||
|
||||
if (package1.equals(package2)) {
|
||||
return n1.compareTo(n2);
|
||||
} else {
|
||||
// Ensure that jdk.* are printed first
|
||||
// This makes it easier to find user defined events at the end.
|
||||
if (Type.SUPER_TYPE_EVENT.equals(t1.getSuperType()) && !package1.equals(package2)) {
|
||||
if (package1.equals("jdk.jfr")) {
|
||||
return -1;
|
||||
}
|
||||
if (package2.equals("jdk.jfr")) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return package1.compareTo(package2);
|
||||
}
|
||||
} else {
|
||||
return Integer.compare(groupValue(t1), groupValue(t2));
|
||||
}
|
||||
}
|
||||
|
||||
int groupValue(Type t) {
|
||||
String superType = t.getSuperType();
|
||||
if (superType == null) {
|
||||
return 1;
|
||||
}
|
||||
if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) {
|
||||
return 3;
|
||||
}
|
||||
if (Type.SUPER_TYPE_SETTING.equals(superType)) {
|
||||
return 4;
|
||||
}
|
||||
if (Type.SUPER_TYPE_EVENT.equals(superType)) {
|
||||
return 5;
|
||||
}
|
||||
return 2; // reserved for enums in the future
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "metadata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
return Collections.singletonList("<file>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Display event metadata, such as labels, descriptions and field layout";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
Path file = getJFRInputFile(options);
|
||||
|
||||
boolean showIds = false;
|
||||
int optionCount = options.size();
|
||||
while (optionCount > 0) {
|
||||
if (acceptOption(options, "--ids")) {
|
||||
showIds = true;
|
||||
}
|
||||
if (optionCount == options.size()) {
|
||||
// No progress made
|
||||
throw new UserSyntaxException("unknown option " + options.peek());
|
||||
}
|
||||
optionCount = options.size();
|
||||
}
|
||||
|
||||
try (PrintWriter pw = new PrintWriter(System.out)) {
|
||||
PrettyWriter prettyWriter = new PrettyWriter(pw);
|
||||
prettyWriter.setShowIds(showIds);
|
||||
try (RecordingFile rf = new RecordingFile(file)) {
|
||||
List<Type> types = RecordingInternals.INSTANCE.readTypes(rf);
|
||||
Collections.sort(types, new TypeComparator());
|
||||
for (Type type : types) {
|
||||
prettyWriter.printType(type);
|
||||
}
|
||||
prettyWriter.flush(true);
|
||||
} catch (IOException ioe) {
|
||||
couldNotReadError(file, ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
634
jdkSrc/jdk8/jdk/jfr/internal/tool/PrettyWriter.java
Normal file
634
jdkSrc/jdk8/jdk/jfr/internal/tool/PrettyWriter.java
Normal file
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.DataAmount;
|
||||
import jdk.jfr.Frequency;
|
||||
import jdk.jfr.MemoryAddress;
|
||||
import jdk.jfr.Percentage;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.consumer.RecordedClass;
|
||||
import jdk.jfr.consumer.RecordedClassLoader;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedFrame;
|
||||
import jdk.jfr.consumer.RecordedMethod;
|
||||
import jdk.jfr.consumer.RecordedObject;
|
||||
import jdk.jfr.consumer.RecordedStackTrace;
|
||||
import jdk.jfr.consumer.RecordedThread;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
/**
|
||||
* Print events in a human-readable format.
|
||||
*
|
||||
* This class is also used by {@link RecordedObject#toString()}
|
||||
*/
|
||||
public final class PrettyWriter extends EventPrintWriter {
|
||||
private static final String TYPE_OLD_OBJECT = Type.TYPES_PREFIX + "OldObject";
|
||||
private final static DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
|
||||
private final static Long ZERO = 0L;
|
||||
private boolean showIds;
|
||||
private RecordedEvent currentEvent;
|
||||
|
||||
public PrettyWriter(PrintWriter destination) {
|
||||
super(destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void print(List<RecordedEvent> events) {
|
||||
for (RecordedEvent e : events) {
|
||||
print(e);
|
||||
flush(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void printType(Type t) {
|
||||
if (showIds) {
|
||||
print("// id: ");
|
||||
println(String.valueOf(t.getId()));
|
||||
}
|
||||
int commentIndex = t.getName().length() + 10;
|
||||
String typeName = t.getName();
|
||||
int index = typeName.lastIndexOf(".");
|
||||
if (index != -1) {
|
||||
println("@Name(\"" + typeName + "\")");
|
||||
}
|
||||
printAnnotations(commentIndex, t.getAnnotationElements());
|
||||
print("class " + typeName.substring(index + 1));
|
||||
String superType = t.getSuperType();
|
||||
if (superType != null) {
|
||||
print(" extends " + superType);
|
||||
}
|
||||
println(" {");
|
||||
indent();
|
||||
boolean first = true;
|
||||
for (ValueDescriptor v : t.getFields()) {
|
||||
printField(commentIndex, v, first);
|
||||
first = false;
|
||||
}
|
||||
retract();
|
||||
println("}");
|
||||
println();
|
||||
}
|
||||
|
||||
private void printField(int commentIndex, ValueDescriptor v, boolean first) {
|
||||
if (!first) {
|
||||
println();
|
||||
}
|
||||
printAnnotations(commentIndex, v.getAnnotationElements());
|
||||
printIndent();
|
||||
Type vType = PrivateAccess.getInstance().getType(v);
|
||||
if (Type.SUPER_TYPE_SETTING.equals(vType.getSuperType())) {
|
||||
print("static ");
|
||||
}
|
||||
print(makeSimpleType(v.getTypeName()));
|
||||
if (v.isArray()) {
|
||||
print("[]");
|
||||
}
|
||||
print(" ");
|
||||
print(v.getName());
|
||||
print(";");
|
||||
printCommentRef(commentIndex, v.getTypeId());
|
||||
}
|
||||
|
||||
private void printCommentRef(int commentIndex, long typeId) {
|
||||
if (showIds) {
|
||||
int column = getColumn();
|
||||
if (column > commentIndex) {
|
||||
print(" ");
|
||||
} else {
|
||||
while (column < commentIndex) {
|
||||
print(" ");
|
||||
column++;
|
||||
}
|
||||
}
|
||||
println(" // id=" + typeId);
|
||||
} else {
|
||||
println();
|
||||
}
|
||||
}
|
||||
|
||||
private void printAnnotations(int commentIndex, List<AnnotationElement> annotations) {
|
||||
for (AnnotationElement a : annotations) {
|
||||
printIndent();
|
||||
print("@");
|
||||
print(makeSimpleType(a.getTypeName()));
|
||||
List<ValueDescriptor> vs = a.getValueDescriptors();
|
||||
if (!vs.isEmpty()) {
|
||||
printAnnotation(a);
|
||||
printCommentRef(commentIndex, a.getTypeId());
|
||||
} else {
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printAnnotation(AnnotationElement a) {
|
||||
StringJoiner sj = new StringJoiner(", ", "(", ")");
|
||||
List<ValueDescriptor> vs = a.getValueDescriptors();
|
||||
for (ValueDescriptor v : vs) {
|
||||
Object o = a.getValue(v.getName());
|
||||
if (vs.size() == 1 && v.getName().equals("value")) {
|
||||
sj.add(textify(o));
|
||||
} else {
|
||||
sj.add(v.getName() + "=" + textify(o));
|
||||
}
|
||||
}
|
||||
print(sj.toString());
|
||||
}
|
||||
|
||||
private String textify(Object o) {
|
||||
if (o.getClass().isArray()) {
|
||||
Object[] array = (Object[]) o;
|
||||
if (array.length == 1) {
|
||||
return quoteIfNeeded(array[0]);
|
||||
}
|
||||
StringJoiner s = new StringJoiner(", ", "{", "}");
|
||||
for (Object ob : array) {
|
||||
s.add(quoteIfNeeded(ob));
|
||||
}
|
||||
return s.toString();
|
||||
} else {
|
||||
return quoteIfNeeded(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String quoteIfNeeded(Object o) {
|
||||
if (o instanceof String) {
|
||||
return "\"" + o + "\"";
|
||||
} else {
|
||||
return String.valueOf(o);
|
||||
}
|
||||
}
|
||||
|
||||
private String makeSimpleType(String typeName) {
|
||||
int index = typeName.lastIndexOf(".");
|
||||
return typeName.substring(index + 1);
|
||||
}
|
||||
|
||||
public void print(RecordedEvent event) {
|
||||
currentEvent = event;
|
||||
print(event.getEventType().getName(), " ");
|
||||
println("{");
|
||||
indent();
|
||||
for (ValueDescriptor v : event.getFields()) {
|
||||
String name = v.getName();
|
||||
if (!isZeroDuration(event, name) && !isLateField(name)) {
|
||||
printFieldValue(event, v);
|
||||
}
|
||||
}
|
||||
if (event.getThread() != null) {
|
||||
printIndent();
|
||||
print(EVENT_THREAD_FIELD + " = ");
|
||||
printThread(event.getThread(), "");
|
||||
}
|
||||
if (event.getStackTrace() != null) {
|
||||
printIndent();
|
||||
print(STACK_TRACE_FIELD + " = ");
|
||||
printStackTrace(event.getStackTrace());
|
||||
}
|
||||
retract();
|
||||
printIndent();
|
||||
println("}");
|
||||
println();
|
||||
}
|
||||
|
||||
private boolean isZeroDuration(RecordedEvent event, String name) {
|
||||
return name.equals("duration") && ZERO.equals(event.getValue("duration"));
|
||||
}
|
||||
|
||||
private void printStackTrace(RecordedStackTrace stackTrace) {
|
||||
println("[");
|
||||
List<RecordedFrame> frames = stackTrace.getFrames();
|
||||
indent();
|
||||
int i = 0;
|
||||
while (i < frames.size() && i < getStackDepth()) {
|
||||
RecordedFrame frame = frames.get(i);
|
||||
if (frame.isJavaFrame()) {
|
||||
printIndent();
|
||||
printValue(frame, null, "");
|
||||
println();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (stackTrace.isTruncated() || i == getStackDepth()) {
|
||||
printIndent();
|
||||
println("...");
|
||||
}
|
||||
retract();
|
||||
printIndent();
|
||||
println("]");
|
||||
}
|
||||
|
||||
public void print(RecordedObject struct, String postFix) {
|
||||
println("{");
|
||||
indent();
|
||||
for (ValueDescriptor v : struct.getFields()) {
|
||||
printFieldValue(struct, v);
|
||||
}
|
||||
retract();
|
||||
printIndent();
|
||||
println("}" + postFix);
|
||||
}
|
||||
|
||||
private void printFieldValue(RecordedObject struct, ValueDescriptor v) {
|
||||
printIndent();
|
||||
print(v.getName(), " = ");
|
||||
printValue(getValue(struct, v), v, "");
|
||||
}
|
||||
|
||||
private void printArray(Object[] array) {
|
||||
println("[");
|
||||
indent();
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
printIndent();
|
||||
printValue(array[i], null, i + 1 < array.length ? ", " : "");
|
||||
}
|
||||
retract();
|
||||
printIndent();
|
||||
println("]");
|
||||
}
|
||||
|
||||
private void printValue(Object value, ValueDescriptor field, String postFix) {
|
||||
if (value == null) {
|
||||
println("N/A" + postFix);
|
||||
return;
|
||||
}
|
||||
if (value instanceof RecordedObject) {
|
||||
if (value instanceof RecordedThread) {
|
||||
printThread((RecordedThread) value, postFix);
|
||||
return;
|
||||
}
|
||||
if (value instanceof RecordedClass) {
|
||||
printClass((RecordedClass) value, postFix);
|
||||
return;
|
||||
}
|
||||
if (value instanceof RecordedClassLoader) {
|
||||
printClassLoader((RecordedClassLoader) value, postFix);
|
||||
return;
|
||||
}
|
||||
if (value instanceof RecordedFrame) {
|
||||
RecordedFrame frame = (RecordedFrame) value;
|
||||
if (frame.isJavaFrame()) {
|
||||
printJavaFrame((RecordedFrame) value, postFix);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (value instanceof RecordedMethod) {
|
||||
println(formatMethod((RecordedMethod) value));
|
||||
return;
|
||||
}
|
||||
if (field.getTypeName().equals(TYPE_OLD_OBJECT)) {
|
||||
printOldObject((RecordedObject) value);
|
||||
return;
|
||||
}
|
||||
print((RecordedObject) value, postFix);
|
||||
return;
|
||||
}
|
||||
if (value.getClass().isArray()) {
|
||||
printArray((Object[]) value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Double) {
|
||||
Double d = (Double) value;
|
||||
if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) {
|
||||
println("N/A");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (value instanceof Float) {
|
||||
Float f = (Float) value;
|
||||
if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) {
|
||||
println("N/A");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
Long l = (Long) value;
|
||||
if (l == Long.MIN_VALUE) {
|
||||
println("N/A");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
Integer i = (Integer) value;
|
||||
if (i == Integer.MIN_VALUE) {
|
||||
println("N/A");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (field.getContentType() != null) {
|
||||
if (printFormatted(field, value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String text = String.valueOf(value);
|
||||
if (value instanceof String) {
|
||||
text = "\"" + text + "\"";
|
||||
}
|
||||
println(text);
|
||||
}
|
||||
|
||||
private void printOldObject(RecordedObject object) {
|
||||
println(" [");
|
||||
indent();
|
||||
printIndent();
|
||||
try {
|
||||
printReferenceChain(object);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// Could not find a field
|
||||
// Not possible to validate fields beforehand using RecordedObject#hasField
|
||||
// since nested objects, for example object.referrer.array.index, requires
|
||||
// an actual array object (which may be null).
|
||||
}
|
||||
retract();
|
||||
printIndent();
|
||||
println("]");
|
||||
}
|
||||
|
||||
private void printReferenceChain(RecordedObject object) {
|
||||
printObject(object, currentEvent.getLong("arrayElements"));
|
||||
for (RecordedObject ref = object.getValue("referrer"); ref != null; ref = object.getValue("referrer")) {
|
||||
long skip = ref.getLong("skip");
|
||||
if (skip > 0) {
|
||||
printIndent();
|
||||
println("...");
|
||||
}
|
||||
String objectHolder = "";
|
||||
long size = Long.MIN_VALUE;
|
||||
RecordedObject array = ref.getValue("array");
|
||||
if (array != null) {
|
||||
long index = array.getLong("index");
|
||||
size = array.getLong("size");
|
||||
objectHolder = "[" + index + "]";
|
||||
}
|
||||
RecordedObject field = ref.getValue("field");
|
||||
if (field != null) {
|
||||
objectHolder = field.getString("name");
|
||||
}
|
||||
printIndent();
|
||||
print(objectHolder);
|
||||
print(" : ");
|
||||
object = ref.getValue("object");
|
||||
if (object != null) {
|
||||
printObject(object, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printObject(RecordedObject object, long arraySize) {
|
||||
RecordedClass clazz = object.getClass("type");
|
||||
if (clazz != null) {
|
||||
String className = clazz.getName();
|
||||
if (className!= null && className.startsWith("[")) {
|
||||
className = decodeDescriptors(className, arraySize > 0 ? Long.toString(arraySize) : "").get(0);
|
||||
}
|
||||
print(className);
|
||||
String description = object.getString("description");
|
||||
if (description != null) {
|
||||
print(" ");
|
||||
print(description);
|
||||
}
|
||||
}
|
||||
println();
|
||||
}
|
||||
|
||||
private void printClassLoader(RecordedClassLoader cl, String postFix) {
|
||||
// Purposely not printing class loader name to avoid cluttered output
|
||||
RecordedClass clazz = cl.getType();
|
||||
print(clazz == null ? "null" : clazz.getName());
|
||||
if (clazz != null) {
|
||||
print(" (");
|
||||
print("id = ");
|
||||
print(String.valueOf(cl.getId()));
|
||||
println(")");
|
||||
}
|
||||
}
|
||||
|
||||
private void printJavaFrame(RecordedFrame f, String postFix) {
|
||||
print(formatMethod(f.getMethod()));
|
||||
int line = f.getLineNumber();
|
||||
if (line >= 0) {
|
||||
print(" line: " + line);
|
||||
}
|
||||
print(postFix);
|
||||
}
|
||||
|
||||
private String formatMethod(RecordedMethod m) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(m.getType().getName());
|
||||
sb.append(".");
|
||||
sb.append(m.getName());
|
||||
sb.append("(");
|
||||
StringJoiner sj = new StringJoiner(", ");
|
||||
String md = m.getDescriptor().replace("/", ".");
|
||||
String parameter = md.substring(1, md.lastIndexOf(")"));
|
||||
for (String qualifiedName : decodeDescriptors(parameter, "")) {
|
||||
String typeName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
|
||||
sj.add(typeName);
|
||||
}
|
||||
sb.append(sj);
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void printClass(RecordedClass clazz, String postFix) {
|
||||
RecordedClassLoader classLoader = clazz.getClassLoader();
|
||||
String classLoaderName = "null";
|
||||
if (classLoader != null) {
|
||||
if (classLoader.getName() != null) {
|
||||
classLoaderName = classLoader.getName();
|
||||
} else {
|
||||
classLoaderName = classLoader.getType().getName();
|
||||
}
|
||||
}
|
||||
String className = clazz.getName();
|
||||
if (className.startsWith("[")) {
|
||||
className = decodeDescriptors(className, "").get(0);
|
||||
}
|
||||
println(className + " (classLoader = " + classLoaderName + ")" + postFix);
|
||||
}
|
||||
|
||||
List<String> decodeDescriptors(String descriptor, String arraySize) {
|
||||
List<String> descriptors = new ArrayList<>();
|
||||
for (int index = 0; index < descriptor.length(); index++) {
|
||||
String arrayBrackets = "";
|
||||
while (descriptor.charAt(index) == '[') {
|
||||
arrayBrackets = arrayBrackets + "[" + arraySize + "]" ;
|
||||
arraySize = "";
|
||||
index++;
|
||||
}
|
||||
char c = descriptor.charAt(index);
|
||||
String type;
|
||||
switch (c) {
|
||||
case 'L':
|
||||
int endIndex = descriptor.indexOf(';', index);
|
||||
type = descriptor.substring(index + 1, endIndex);
|
||||
index = endIndex;
|
||||
break;
|
||||
case 'I':
|
||||
type = "int";
|
||||
break;
|
||||
case 'J':
|
||||
type = "long";
|
||||
break;
|
||||
case 'Z':
|
||||
type = "boolean";
|
||||
break;
|
||||
case 'D':
|
||||
type = "double";
|
||||
break;
|
||||
case 'F':
|
||||
type = "float";
|
||||
break;
|
||||
case 'S':
|
||||
type = "short";
|
||||
break;
|
||||
case 'C':
|
||||
type = "char";
|
||||
break;
|
||||
case 'B':
|
||||
type = "byte";
|
||||
break;
|
||||
default:
|
||||
type = "<unknown-descriptor-type>";
|
||||
}
|
||||
descriptors.add(type + arrayBrackets);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
private void printThread(RecordedThread thread, String postFix) {
|
||||
long javaThreadId = thread.getJavaThreadId();
|
||||
if (javaThreadId > 0) {
|
||||
println("\"" + thread.getJavaName() + "\" (javaThreadId = " + thread.getJavaThreadId() + ")" + postFix);
|
||||
} else {
|
||||
println("\"" + thread.getOSName() + "\" (osThreadId = " + thread.getOSThreadId() + ")" + postFix);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean printFormatted(ValueDescriptor field, Object value) {
|
||||
if (value instanceof Duration) {
|
||||
Duration d = (Duration) value;
|
||||
if (d.getSeconds() == Long.MIN_VALUE && d.getNano() == 0) {
|
||||
println("N/A");
|
||||
return true;
|
||||
}
|
||||
double s = d.getNano() / 1000_000_000.0 + (int) (d.getSeconds() % 60);
|
||||
if (s < 1.0) {
|
||||
if (s < 0.001) {
|
||||
println(String.format("%.3f", s * 1_000_000) + " us");
|
||||
} else {
|
||||
println(String.format("%.3f", s * 1_000) + " ms");
|
||||
}
|
||||
} else {
|
||||
if (s < 1000.0) {
|
||||
println(String.format("%.3f", s) + " s");
|
||||
} else {
|
||||
println(String.format("%.0f", s) + " s");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (value instanceof OffsetDateTime) {
|
||||
OffsetDateTime odt = (OffsetDateTime) value;
|
||||
if (odt.equals(OffsetDateTime.MIN)) {
|
||||
println("N/A");
|
||||
return true;
|
||||
}
|
||||
println(TIME_FORMAT.format(odt));
|
||||
return true;
|
||||
}
|
||||
Percentage percentage = field.getAnnotation(Percentage.class);
|
||||
if (percentage != null) {
|
||||
if (value instanceof Number) {
|
||||
double d = ((Number) value).doubleValue();
|
||||
println(String.format("%.2f", d * 100) + "%");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
DataAmount dataAmount = field.getAnnotation(DataAmount.class);
|
||||
if (dataAmount != null) {
|
||||
if (value instanceof Number) {
|
||||
Number n = (Number) value;
|
||||
long amount = n.longValue();
|
||||
if (field.getAnnotation(Frequency.class) != null) {
|
||||
if (dataAmount.value().equals(DataAmount.BYTES)) {
|
||||
println(Utils.formatBytesPerSecond(amount));
|
||||
return true;
|
||||
}
|
||||
if (dataAmount.value().equals(DataAmount.BITS)) {
|
||||
println(Utils.formatBitsPerSecond(amount));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (dataAmount.value().equals(DataAmount.BYTES)) {
|
||||
println(Utils.formatBytes(amount));
|
||||
return true;
|
||||
}
|
||||
if (dataAmount.value().equals(DataAmount.BITS)) {
|
||||
println(Utils.formatBits(amount));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MemoryAddress memoryAddress = field.getAnnotation(MemoryAddress.class);
|
||||
if (memoryAddress != null) {
|
||||
if (value instanceof Number) {
|
||||
long d = ((Number) value).longValue();
|
||||
println(String.format("0x%08X", d));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Frequency frequency = field.getAnnotation(Frequency.class);
|
||||
if (frequency != null) {
|
||||
if (value instanceof Number) {
|
||||
println(value + " Hz");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setShowIds(boolean showIds) {
|
||||
this.showIds = showIds;
|
||||
}
|
||||
}
|
||||
282
jdkSrc/jdk8/jdk/jfr/internal/tool/Print.java
Normal file
282
jdkSrc/jdk8/jdk/jfr/internal/tool/Print.java
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
|
||||
final class Print extends Command {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "print";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("[--xml|--json]");
|
||||
list.add("[--categories <filter>]");
|
||||
list.add("[--events <filter>]");
|
||||
list.add("[--stack-depth <depth>]");
|
||||
list.add("<file>");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return "Print contents of a recording file";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return getTitle() + ". See 'jfr help print' for details.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayOptionUsage(PrintStream stream) {
|
||||
stream.println(" --xml Print recording in XML format");
|
||||
stream.println();
|
||||
stream.println(" --json Print recording in JSON format");
|
||||
stream.println();
|
||||
stream.println(" --categories <filter> Select events matching a category name.");
|
||||
stream.println(" The filter is a comma-separated list of names,");
|
||||
stream.println(" simple and/or qualified, and/or quoted glob patterns");
|
||||
stream.println();
|
||||
stream.println(" --events <filter> Select events matching an event name.");
|
||||
stream.println(" The filter is a comma-separated list of names,");
|
||||
stream.println(" simple and/or qualified, and/or quoted glob patterns");
|
||||
stream.println();
|
||||
stream.println(" --stack-depth <depth> Number of frames in stack traces, by default 5");
|
||||
stream.println();
|
||||
stream.println(" <file> Location of the recording file (.jfr)");
|
||||
stream.println();
|
||||
stream.println();
|
||||
stream.println("Example usage:");
|
||||
stream.println();
|
||||
stream.println(" jfr print --events OldObjectSample recording.jfr");
|
||||
stream.println();
|
||||
stream.println(" jfr print --events CPULoad,GarbageCollection recording.jfr");
|
||||
stream.println();
|
||||
stream.println(" jfr print --categories \"GC,JVM,Java*\" recording.jfr");
|
||||
stream.println();
|
||||
stream.println(" jfr print --events \"jdk.*\" --stack-depth 64 recording.jfr");
|
||||
stream.println();
|
||||
stream.println(" jfr print --json --events CPULoad recording.jfr");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
Path file = getJFRInputFile(options);
|
||||
PrintWriter pw = new PrintWriter(System.out, false);
|
||||
Predicate<EventType> eventFilter = null;
|
||||
int stackDepth = 5;
|
||||
EventPrintWriter eventWriter = null;
|
||||
int optionCount = options.size();
|
||||
boolean foundEventFilter = false;
|
||||
boolean foundCategoryFilter = false;
|
||||
while (optionCount > 0) {
|
||||
if (acceptFilterOption(options, "--events")) {
|
||||
if (foundEventFilter) {
|
||||
throw new UserSyntaxException("use --events event1,event2,event3 to include multiple events");
|
||||
}
|
||||
foundEventFilter = true;
|
||||
String filter = options.remove();
|
||||
warnForWildcardExpansion("--events", filter);
|
||||
eventFilter = addEventFilter(filter, eventFilter);
|
||||
}
|
||||
if (acceptFilterOption(options, "--categories")) {
|
||||
if (foundCategoryFilter) {
|
||||
throw new UserSyntaxException("use --categories category1,category2 to include multiple categories");
|
||||
}
|
||||
foundCategoryFilter = true;
|
||||
String filter = options.remove();
|
||||
warnForWildcardExpansion("--categories", filter);
|
||||
eventFilter = addCategoryFilter(filter, eventFilter);
|
||||
}
|
||||
if (acceptOption(options, "--stack-depth")) {
|
||||
String value = options.pop();
|
||||
try {
|
||||
stackDepth = Integer.parseInt(value);
|
||||
if (stackDepth < 0) {
|
||||
throw new UserSyntaxException("stack depth must be zero or a positive integer.");
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new UserSyntaxException("not a valid value for --stack-depth");
|
||||
}
|
||||
}
|
||||
if (acceptFormatterOption(options, eventWriter, "--json")) {
|
||||
eventWriter = new JSONWriter(pw);
|
||||
}
|
||||
if (acceptFormatterOption(options, eventWriter, "--xml")) {
|
||||
eventWriter = new XMLWriter(pw);
|
||||
}
|
||||
if (optionCount == options.size()) {
|
||||
// No progress made
|
||||
checkCommonError(options, "--event", "--events");
|
||||
checkCommonError(options, "--category", "--categories");
|
||||
throw new UserSyntaxException("unknown option " + options.peek());
|
||||
}
|
||||
optionCount = options.size();
|
||||
}
|
||||
if (eventWriter == null) {
|
||||
eventWriter = new PrettyWriter(pw); // default to pretty printer
|
||||
}
|
||||
eventWriter.setStackDepth(stackDepth);
|
||||
if (eventFilter != null) {
|
||||
eventFilter = addCache(eventFilter, eventType -> eventType.getId());
|
||||
eventWriter.setEventFilter(eventFilter);
|
||||
}
|
||||
try {
|
||||
eventWriter.print(file);
|
||||
} catch (IOException ioe) {
|
||||
couldNotReadError(file, ioe);
|
||||
}
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
private void checkCommonError(Deque<String> options, String typo, String correct) throws UserSyntaxException {
|
||||
if (typo.equals(options.peek())) {
|
||||
throw new UserSyntaxException("unknown option " + typo + ", did you mean " + correct + "?");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean acceptFormatterOption(Deque<String> options, EventPrintWriter eventWriter, String expected) throws UserSyntaxException {
|
||||
if (expected.equals(options.peek())) {
|
||||
if (eventWriter != null) {
|
||||
throw new UserSyntaxException("only one format can be specified at a time");
|
||||
}
|
||||
options.remove();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T, X> Predicate<T> addCache(final Predicate<T> filter, Function<T, X> cacheFunction) {
|
||||
Map<X, Boolean> cache = new HashMap<>();
|
||||
return t -> cache.computeIfAbsent(cacheFunction.apply(t), x -> filter.test(t));
|
||||
}
|
||||
|
||||
private static <T> Predicate<T> recurseIfPossible(Predicate<T> filter) {
|
||||
return x -> filter != null && filter.test(x);
|
||||
}
|
||||
|
||||
private static Predicate<EventType> addCategoryFilter(String filterText, Predicate<EventType> eventFilter) throws UserSyntaxException {
|
||||
List<String> filters = explodeFilter(filterText);
|
||||
Predicate<EventType> newFilter = recurseIfPossible(eventType -> {
|
||||
for (String category : eventType.getCategoryNames()) {
|
||||
for (String filter : filters) {
|
||||
if (match(category, filter)) {
|
||||
return true;
|
||||
}
|
||||
if (category.contains(" ") && acronomify(category).equals(filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return eventFilter == null ? newFilter : eventFilter.or(newFilter);
|
||||
}
|
||||
|
||||
private static String acronomify(String multipleWords) {
|
||||
boolean newWord = true;
|
||||
String acronym = "";
|
||||
for (char c : multipleWords.toCharArray()) {
|
||||
if (newWord) {
|
||||
if (Character.isAlphabetic(c) && Character.isUpperCase(c)) {
|
||||
acronym += c;
|
||||
}
|
||||
}
|
||||
newWord = Character.isWhitespace(c);
|
||||
}
|
||||
return acronym;
|
||||
}
|
||||
|
||||
private static Predicate<EventType> addEventFilter(String filterText, final Predicate<EventType> eventFilter) throws UserSyntaxException {
|
||||
List<String> filters = explodeFilter(filterText);
|
||||
Predicate<EventType> newFilter = recurseIfPossible(eventType -> {
|
||||
for (String filter : filters) {
|
||||
String fullEventName = eventType.getName();
|
||||
if (match(fullEventName, filter)) {
|
||||
return true;
|
||||
}
|
||||
String eventName = fullEventName.substring(fullEventName.lastIndexOf(".") + 1);
|
||||
if (match(eventName, filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return eventFilter == null ? newFilter : eventFilter.or(newFilter);
|
||||
}
|
||||
|
||||
private static boolean match(String text, String filter) {
|
||||
if (filter.length() == 0) {
|
||||
// empty filter string matches if string is empty
|
||||
return text.length() == 0;
|
||||
}
|
||||
if (filter.charAt(0) == '*') { // recursive check
|
||||
filter = filter.substring(1);
|
||||
for (int n = 0; n <= text.length(); n++) {
|
||||
if (match(text.substring(n), filter))
|
||||
return true;
|
||||
}
|
||||
} else if (text.length() == 0) {
|
||||
// empty string and non-empty filter does not match
|
||||
return false;
|
||||
} else if (filter.charAt(0) == '?') {
|
||||
// eat any char and move on
|
||||
return match(text.substring(1), filter.substring(1));
|
||||
} else if (filter.charAt(0) == text.charAt(0)) {
|
||||
// eat chars and move on
|
||||
return match(text.substring(1), filter.substring(1));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<String> explodeFilter(String filter) throws UserSyntaxException {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String s : filter.split(",")) {
|
||||
s = s.trim();
|
||||
if (!s.isEmpty()) {
|
||||
list.add(s);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
121
jdkSrc/jdk8/jdk/jfr/internal/tool/StructuredWriter.java
Normal file
121
jdkSrc/jdk8/jdk/jfr/internal/tool/StructuredWriter.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
abstract class StructuredWriter {
|
||||
private final static String LINE_SEPARATOR = String.format("%n");
|
||||
|
||||
private final PrintWriter out;
|
||||
private final StringBuilder builder = new StringBuilder(4000);
|
||||
|
||||
private char[] indentionArray = new char[0];
|
||||
private int indent = 0;
|
||||
private int column;
|
||||
// print first event immediately so tool feels responsive
|
||||
private boolean first = true;
|
||||
|
||||
StructuredWriter(PrintWriter p) {
|
||||
out = p;
|
||||
}
|
||||
|
||||
final protected int getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
// Flush to print writer
|
||||
public final void flush(boolean hard) {
|
||||
if (hard) {
|
||||
out.print(builder.toString());
|
||||
builder.setLength(0);
|
||||
return;
|
||||
}
|
||||
if (first || builder.length() > 100_000) {
|
||||
out.print(builder.toString());
|
||||
builder.setLength(0);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
final public void printIndent() {
|
||||
builder.append(indentionArray, 0, indent);
|
||||
column += indent;
|
||||
}
|
||||
|
||||
final public void println() {
|
||||
builder.append(LINE_SEPARATOR);
|
||||
column = 0;
|
||||
}
|
||||
|
||||
final public void print(String... texts) {
|
||||
for (String text : texts) {
|
||||
print(text);
|
||||
}
|
||||
}
|
||||
|
||||
final public void printAsString(Object o) {
|
||||
print(String.valueOf(o));
|
||||
}
|
||||
|
||||
final public void print(String text) {
|
||||
builder.append(text);
|
||||
column += text.length();
|
||||
}
|
||||
|
||||
final public void print(char c) {
|
||||
builder.append(c);
|
||||
column++;
|
||||
}
|
||||
|
||||
final public void print(int value) {
|
||||
print(String.valueOf(value));
|
||||
}
|
||||
|
||||
final public void indent() {
|
||||
indent += 2;
|
||||
updateIndent();
|
||||
}
|
||||
|
||||
final public void retract() {
|
||||
indent -= 2;
|
||||
updateIndent();
|
||||
}
|
||||
|
||||
final public void println(String text) {
|
||||
print(text);
|
||||
println();
|
||||
}
|
||||
|
||||
private void updateIndent() {
|
||||
if (indent > indentionArray.length) {
|
||||
indentionArray = new char[indent];
|
||||
for (int i = 0; i < indentionArray.length; i++) {
|
||||
indentionArray[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
162
jdkSrc/jdk8/jdk/jfr/internal/tool/Summary.java
Normal file
162
jdkSrc/jdk8/jdk/jfr/internal/tool/Summary.java
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.internal.MetadataDescriptor;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
|
||||
final class Summary extends Command {
|
||||
private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withLocale(Locale.UK).withZone(ZoneOffset.UTC);
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "summary";
|
||||
}
|
||||
|
||||
private static class Statistics {
|
||||
Statistics(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
String name;
|
||||
long count;
|
||||
long size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
return Collections.singletonList("<file>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayOptionUsage(PrintStream stream) {
|
||||
stream.println(" <file> Location of the recording file (.jfr) to display information about");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Display general information about a recording file (.jfr)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
ensureMaxArgumentCount(options, 1);
|
||||
Path p = getJFRInputFile(options);
|
||||
try {
|
||||
printInformation(p);
|
||||
} catch (IOException e) {
|
||||
couldNotReadError(p, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void printInformation(Path p) throws IOException {
|
||||
long totalDuration = 0;
|
||||
long chunks = 0;
|
||||
|
||||
try (RecordingInput input = new RecordingInput(p.toFile())) {
|
||||
ChunkHeader first = new ChunkHeader(input);
|
||||
ChunkHeader ch = first;
|
||||
String eventPrefix = Type.EVENT_NAME_PREFIX;
|
||||
if (first.getMajor() == 1) {
|
||||
eventPrefix = "com.oracle.jdk.";
|
||||
}
|
||||
HashMap<Long, Statistics> stats = new HashMap<>();
|
||||
stats.put(0L, new Statistics(eventPrefix + "Metadata"));
|
||||
stats.put(1L, new Statistics(eventPrefix + "CheckPoint"));
|
||||
int minWidth = 0;
|
||||
while (true) {
|
||||
long chunkEnd = ch.getEnd();
|
||||
MetadataDescriptor md = ch.readMetadata();
|
||||
|
||||
for (EventType eventType : md.getEventTypes()) {
|
||||
stats.computeIfAbsent(eventType.getId(), (e) -> new Statistics(eventType.getName()));
|
||||
minWidth = Math.max(minWidth, eventType.getName().length());
|
||||
}
|
||||
|
||||
totalDuration += ch.getDurationNanos();
|
||||
chunks++;
|
||||
input.position(ch.getEventStart());
|
||||
while (input.position() < chunkEnd) {
|
||||
long pos = input.position();
|
||||
int size = input.readInt();
|
||||
long eventTypeId = input.readLong();
|
||||
Statistics s = stats.get(eventTypeId);
|
||||
if (s != null) {
|
||||
s.count++;
|
||||
s.size += size;
|
||||
}
|
||||
input.position(pos + size);
|
||||
}
|
||||
if (ch.isLastChunk()) {
|
||||
break;
|
||||
}
|
||||
ch = ch.nextHeader();
|
||||
}
|
||||
println();
|
||||
long epochSeconds = first.getStartNanos() / 1_000_000_000L;
|
||||
long adjustNanos = first.getStartNanos() - epochSeconds * 1_000_000_000L;
|
||||
println(" Version: " + first.getMajor() + "." + first.getMinor());
|
||||
println(" Chunks: " + chunks);
|
||||
println(" Start: " + DATE_FORMAT.format(Instant.ofEpochSecond(epochSeconds, adjustNanos)) + " (UTC)");
|
||||
println(" Duration: " + (totalDuration + 500_000_000) / 1_000_000_000 + " s");
|
||||
|
||||
List<Statistics> statsList = new ArrayList<>(stats.values());
|
||||
Collections.sort(statsList, (u, v) -> Long.compare(v.count, u.count));
|
||||
println();
|
||||
String header = " Count Size (bytes) ";
|
||||
String typeHeader = " Event Type";
|
||||
minWidth = Math.max(minWidth, typeHeader.length());
|
||||
println(typeHeader + pad(minWidth - typeHeader.length(), ' ') + header);
|
||||
println(pad(minWidth + header.length(), '='));
|
||||
for (Statistics s : statsList) {
|
||||
System.out.printf(" %-" + minWidth + "s%10d %12d\n", s.name, s.count, s.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String pad(int count, char c) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
49
jdkSrc/jdk8/jdk/jfr/internal/tool/UserDataException.java
Normal file
49
jdkSrc/jdk8/jdk/jfr/internal/tool/UserDataException.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 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.tool;
|
||||
|
||||
/**
|
||||
* Exception that is thrown if there is something wrong with the input, for instance
|
||||
* a file that can't be read or a numerical value that is out of range.
|
||||
* <p>
|
||||
* When this exception is thrown, a user will typically not want to see the
|
||||
* command line syntax, but instead information about what was wrong with the
|
||||
* input.
|
||||
*/
|
||||
final class UserDataException extends Exception {
|
||||
private static final long serialVersionUID = 6656457380115167810L;
|
||||
/**
|
||||
* The error message.
|
||||
*
|
||||
* The first letter should not be capitalized, so a context can be printed prior
|
||||
* to the error message.
|
||||
*
|
||||
* @param errorMessage
|
||||
*/
|
||||
public UserDataException(String errorMessage) {
|
||||
super(errorMessage);
|
||||
}
|
||||
}
|
||||
45
jdkSrc/jdk8/jdk/jfr/internal/tool/UserSyntaxException.java
Normal file
45
jdkSrc/jdk8/jdk/jfr/internal/tool/UserSyntaxException.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 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.tool;
|
||||
|
||||
/**
|
||||
* Exception that is thrown if options don't follow the syntax of the command.
|
||||
*/
|
||||
final class UserSyntaxException extends Exception {
|
||||
private static final long serialVersionUID = 3437009454344160933L;
|
||||
|
||||
/**
|
||||
* The error message.
|
||||
*
|
||||
* The first letter should not be capitalized, so a context can be printed prior
|
||||
* to the error message.
|
||||
*
|
||||
* @param errorMessage
|
||||
*/
|
||||
public UserSyntaxException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
51
jdkSrc/jdk8/jdk/jfr/internal/tool/Version.java
Normal file
51
jdkSrc/jdk8/jdk/jfr/internal/tool/Version.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 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.tool;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
final class Version extends Command {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "version";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Display version of the jfr tool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Deque<String> options) {
|
||||
System.out.println("1.0");
|
||||
}
|
||||
|
||||
protected List<String> getAliases() {
|
||||
return Arrays.asList("--version");
|
||||
}
|
||||
}
|
||||
204
jdkSrc/jdk8/jdk/jfr/internal/tool/XMLWriter.java
Normal file
204
jdkSrc/jdk8/jdk/jfr/internal/tool/XMLWriter.java
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.tool;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedFrame;
|
||||
import jdk.jfr.consumer.RecordedObject;
|
||||
|
||||
final class XMLWriter extends EventPrintWriter {
|
||||
public XMLWriter(PrintWriter destination) {
|
||||
super(destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printBegin() {
|
||||
println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
println("<recording xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
|
||||
indent();
|
||||
printIndent();
|
||||
println("<events>");
|
||||
indent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printEnd() {
|
||||
retract();
|
||||
printIndent();
|
||||
println("</events>");
|
||||
retract();
|
||||
println("</recording>");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void print(List<RecordedEvent> events) {
|
||||
for (RecordedEvent event : events) {
|
||||
printEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void printEvent(RecordedEvent event) {
|
||||
EventType type = event.getEventType();
|
||||
printIndent();
|
||||
print("<event");
|
||||
printAttribute("type", type.getName());
|
||||
print(">");
|
||||
println();
|
||||
indent();
|
||||
for (ValueDescriptor v : event.getFields()) {
|
||||
printValueDescriptor(v, getValue(event, v), -1);
|
||||
}
|
||||
retract();
|
||||
printIndent();
|
||||
println("</event>");
|
||||
println();
|
||||
}
|
||||
|
||||
private void printAttribute(String name, String value) {
|
||||
print(" ");
|
||||
print(name); // Only known strings
|
||||
print("=\"");
|
||||
printEscaped(value);
|
||||
print("\"");
|
||||
}
|
||||
|
||||
public void printObject(RecordedObject struct) {
|
||||
println();
|
||||
indent();
|
||||
for (ValueDescriptor v : struct.getFields()) {
|
||||
printValueDescriptor(v, getValue(struct, v), -1);
|
||||
}
|
||||
retract();
|
||||
}
|
||||
|
||||
private void printArray(ValueDescriptor v, Object[] array) {
|
||||
println();
|
||||
indent();
|
||||
int depth = 0;
|
||||
for (int index = 0; index < array.length; index++) {
|
||||
Object arrayElement = array[index];
|
||||
if (!(arrayElement instanceof RecordedFrame) || depth < getStackDepth()) {
|
||||
printValueDescriptor(v, array[index], index);
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
retract();
|
||||
}
|
||||
|
||||
private void printValueDescriptor(ValueDescriptor vd, Object value, int index) {
|
||||
boolean arrayElement = index != -1;
|
||||
String name = arrayElement ? null : vd.getName();
|
||||
if (vd.isArray() && !arrayElement) {
|
||||
if (printBeginElement("array", name, value, index)) {
|
||||
printArray(vd, (Object[]) value);
|
||||
printIndent();
|
||||
printEndElement("array");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!vd.getFields().isEmpty()) {
|
||||
if (printBeginElement("struct", name, value, index)) {
|
||||
printObject((RecordedObject) value);
|
||||
printIndent();
|
||||
printEndElement("struct");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (printBeginElement("value", name, value, index)) {
|
||||
printEscaped(String.valueOf(value));
|
||||
printEndElement("value");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean printBeginElement(String elementName, String name, Object value, int index) {
|
||||
printIndent();
|
||||
print("<", elementName);
|
||||
if (name != null) {
|
||||
printAttribute("name", name);
|
||||
}
|
||||
if (index != -1) {
|
||||
printAttribute("index", Integer.toString(index));
|
||||
}
|
||||
if (value == null) {
|
||||
printAttribute("xsi:nil", "true");
|
||||
println("/>");
|
||||
return false;
|
||||
}
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] array = (Object[]) value;
|
||||
printAttribute("size", Integer.toString(array.length));
|
||||
}
|
||||
print(">");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void printEndElement(String elementName) {
|
||||
print("</");
|
||||
print(elementName);
|
||||
println(">");
|
||||
}
|
||||
|
||||
private void printEscaped(String text) {
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
printEscaped(text.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void printEscaped(char c) {
|
||||
if (c == 34) {
|
||||
print(""");
|
||||
return;
|
||||
}
|
||||
if (c == 38) {
|
||||
print("&");
|
||||
return;
|
||||
}
|
||||
if (c == 39) {
|
||||
print("'");
|
||||
return;
|
||||
}
|
||||
if (c == 60) {
|
||||
print("<");
|
||||
return;
|
||||
}
|
||||
if (c == 62) {
|
||||
print(">");
|
||||
return;
|
||||
}
|
||||
if (c > 0x7F) {
|
||||
print("&#");
|
||||
print((int) c);
|
||||
print(';');
|
||||
return;
|
||||
}
|
||||
print(c);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user