feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,174 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Parses a chunk.
*
*/
final class ChunkParser {
private static final long CONSTANT_POOL_TYPE_ID = 1;
private final RecordingInput input;
private final LongMap<Parser> parsers;
private final ChunkHeader chunkHeader;
private final long absoluteChunkEnd;
private final MetadataDescriptor metadata;
private final LongMap<Type> typeMap;
private final TimeConverter timeConverter;
public ChunkParser(RecordingInput input) throws IOException {
this(new ChunkHeader(input));
}
private ChunkParser(ChunkHeader header) throws IOException {
this.input = header.getInput();
this.chunkHeader = header;
this.metadata = header.readMetadata();
this.absoluteChunkEnd = header.getEnd();
this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
ParserFactory factory = new ParserFactory(metadata, timeConverter);
LongMap<ConstantMap> constantPools = factory.getConstantPools();
parsers = factory.getParsers();
typeMap = factory.getTypeMap();
fillConstantPools(parsers, constantPools);
constantPools.forEach(ConstantMap::setIsResolving);
constantPools.forEach(ConstantMap::resolve);
constantPools.forEach(ConstantMap::setResolved);
input.position(chunkHeader.getEventStart());
}
public RecordedEvent readEvent() throws IOException {
while (input.position() < absoluteChunkEnd) {
long pos = input.position();
int size = input.readInt();
if (size == 0) {
throw new IOException("Event can't have zero size");
}
long typeId = input.readLong();
if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0)
Parser ep = parsers.get(typeId);
if (ep instanceof EventParser) {
return (RecordedEvent) ep.parse(input);
}
}
input.position(pos + size);
}
return null;
}
private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException {
long nextCP = chunkHeader.getAbsoluteChunkStart();
long deltaToNext = chunkHeader.getConstantPoolPosition();
while (deltaToNext != 0) {
nextCP += deltaToNext;
input.position(nextCP);
final long position = nextCP;
int size = input.readInt(); // size
long typeId = input.readLong();
if (typeId != CONSTANT_POOL_TYPE_ID) {
throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId);
}
input.readLong(); // timestamp
input.readLong(); // duration
deltaToNext = input.readLong();
final long delta = deltaToNext;
boolean flush = input.readBoolean();
int poolCount = input.readInt();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
});
for (int i = 0; i < poolCount; i++) {
long id = input.readLong(); // type id
ConstantMap pool = constantPools.get(id);
Type type = typeMap.get(id);
if (pool == null) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
if (type == null) {
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]");
}
pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
constantPools.put(type.getId(), pool);
}
Parser parser = typeParser.get(id);
if (parser == null) {
throw new IOException("Could not find constant pool type with id = " + id);
}
try {
int count = input.readInt();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]");
for (int j = 0; j < count; j++) {
long key = input.readLong();
Object value = parser.parse(input);
pool.put(key, value);
}
} catch (Exception e) {
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e);
}
}
if (input.position() != nextCP + size) {
throw new IOException("Size of check point event doesn't match content");
}
}
}
private String getName(long id) {
Type type = typeMap.get(id);
return type == null ? ("unknown(" + id + ")") : type.getName();
}
public Collection<Type> getTypes() {
return metadata.getTypes();
}
public List<EventType> getEventTypes() {
return metadata.getEventTypes();
}
public boolean isLastChunk() {
return chunkHeader.isLastChunk();
}
public ChunkParser nextChunkParser() throws IOException {
return new ChunkParser(chunkHeader.nextHeader());
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import java.util.ArrayList;
import java.util.List;
/**
* Holds mapping between a set of keys and their corresponding object.
*
* If the type is a known type, i.e. {@link RecordedThread}, an
* {@link ObjectFactory} can be supplied which will instantiate a typed object.
*/
final class ConstantMap {
private final static class Reference {
private final long key;
private final ConstantMap pool;
Reference(ConstantMap pool, long key) {
this.pool = pool;
this.key = key;
}
Object resolve() {
return pool.get(key);
}
}
private final ObjectFactory<?> factory;
private final LongMap<Object> objects;
private LongMap<Boolean> isResolving;
private boolean allResolved;
private String name;
ConstantMap(ObjectFactory<?> factory, String name) {
this.name = name;
this.objects = new LongMap<>();
this.factory = factory;
}
Object get(long id) {
// fast path, all objects in pool resolved
if (allResolved) {
return objects.get(id);
}
// referenced from a pool, deal with this later
if (isResolving == null) {
return new Reference(this, id);
}
Boolean beingResolved = isResolving.get(id);
// we are resolved (but not the whole pool)
if (Boolean.FALSE.equals(beingResolved)) {
return objects.get(id);
}
// resolving ourself, abort to avoid infinite recursion
if (Boolean.TRUE.equals(beingResolved)) {
return null;
}
// resolve me!
isResolving.put(id, Boolean.TRUE);
Object resolved = resolve(objects.get(id));
isResolving.put(id, Boolean.FALSE);
if (factory != null) {
Object factorized = factory.createObject(id, resolved);
objects.put(id, factorized);
return factorized;
} else {
objects.put(id, resolved);
return resolved;
}
}
private static Object resolve(Object o) {
if (o instanceof Reference) {
return resolve(((Reference) o).resolve());
}
if (o != null && o.getClass().isArray()) {
final Object[] array = (Object[]) o;
for (int i = 0; i < array.length; i++) {
array[i] = resolve(array[i]);
}
return array;
}
return o;
}
public void resolve() {
List<Long> keyList = new ArrayList<>();
objects.keys().forEachRemaining(keyList::add);
for (Long l : keyList) {
get(l);
}
}
public void put(long key, Object value) {
objects.put(key, value);
}
public void setIsResolving() {
isResolving = new LongMap<>();
}
public void setResolved() {
allResolved = true;
isResolving = null; // pool finished, release memory
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
import java.io.IOException;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Parses an event and returns a {@link RecordedEvent}.
*
*/
final class EventParser extends Parser {
private final Parser[] parsers;
private final EventType eventType;
private final TimeConverter timeConverter;
private final boolean hasDuration;
private final List<ValueDescriptor> valueDescriptors;
EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
this.timeConverter = timeConverter;
this.parsers = parsers;
this.eventType = type;
this.hasDuration = type.getField(FIELD_DURATION) != null;
this.valueDescriptors = type.getFields();
}
@Override
public Object parse(RecordingInput input) throws IOException {
Object[] values = new Object[parsers.length];
for (int i = 0; i < parsers.length; i++) {
values[i] = parsers[i].parse(input);
}
Long startTicks = (Long) values[0];
long startTime = timeConverter.convertTimestamp(startTicks);
if (hasDuration) {
long durationTicks = (Long) values[1];
long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
} else {
return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import java.util.HashMap;
import java.util.Iterator;
/**
* Commonly used data structure for looking up objects given an id (long value)
*
* TODO: Implement without using Map and Long objects, to minimize allocation
*
* @param <T>
*/
final class LongMap<T> implements Iterable<T> {
private final HashMap<Long, T> map;
LongMap() {
map = new HashMap<>(101);
}
void put(long id, T object) {
map.put(id, object);
}
T get(long id) {
return map.get(id);
}
@Override
public Iterator<T> iterator() {
return map.values().iterator();
}
Iterator<Long> keys() {
return map.keySet().iterator();
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* Abstract factory for creating specialized types
*/
abstract class ObjectFactory<T> {
final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
public static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
switch (type.getName()) {
case "java.lang.Thread":
return RecordedThread.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "StackFrame":
case TYPE_PREFIX_VERSION_2 + "StackFrame":
return RecordedFrame.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "Method":
case TYPE_PREFIX_VERSION_2 + "Method":
return RecordedMethod.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
return RecordedThreadGroup.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "StackTrace":
case TYPE_PREFIX_VERSION_2 + "StackTrace":
return RecordedStackTrace.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "ClassLoader":
case TYPE_PREFIX_VERSION_2 + "ClassLoader":
return RecordedClassLoader.createFactory(type, timeConverter);
case "java.lang.Class":
return RecordedClass.createFactory(type, timeConverter);
}
return null;
}
private final List<ValueDescriptor> valueDescriptors;
ObjectFactory(Type type) {
this.valueDescriptors = type.getFields();
}
T createObject(long id, Object value) {
if (value == null) {
return null;
}
if (value instanceof Object[]) {
return createTyped(valueDescriptors, id, (Object[]) value);
}
throw new InternalError("Object factory must have struct type");
}
abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import java.io.IOException;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Base class for parsing data from a {@link RecordingInput}.
*/
abstract class Parser {
/**
* Parses data from a {@link RecordingInput} and return an object.
*
* @param input input to read from
* @return an object
* @throws IOException if operation couldn't be completed due to I/O
* problems
*/
abstract Object parse(RecordingInput input) throws IOException;
}

View File

@@ -0,0 +1,304 @@
/*
* 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.consumer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Class that create parsers suitable for reading events and constant pools
*/
final class ParserFactory {
private final LongMap<Parser> parsers = new LongMap<>();
private final TimeConverter timeConverter;
private final LongMap<Type> types = new LongMap<>();
private final LongMap<ConstantMap> constantPools;
public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException {
this.constantPools = new LongMap<>();
this.timeConverter = timeConverter;
for (Type t : metadata.getTypes()) {
types.put(t.getId(), t);
}
for (Type t : types) {
if (!t.getFields().isEmpty()) { // Avoid primitives
CompositeParser cp = createCompositeParser(t);
if (t.isSimpleType()) { // Reduce to nested parser
parsers.put(t.getId(), cp.parsers[0]);
}
}
}
// Override event types with event parsers
for (EventType t : metadata.getEventTypes()) {
parsers.put(t.getId(), createEventParser(t));
}
}
public LongMap<Parser> getParsers() {
return parsers;
}
public LongMap<ConstantMap> getConstantPools() {
return constantPools;
}
public LongMap<Type> getTypeMap() {
return types;
}
private EventParser createEventParser(EventType eventType) throws IOException {
List<Parser> parsers = new ArrayList<Parser>();
for (ValueDescriptor f : eventType.getFields()) {
parsers.add(createParser(f));
}
return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
}
private Parser createParser(ValueDescriptor v) throws IOException {
boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
if (v.isArray()) {
Type valueType = PrivateAccess.getInstance().getType(v);
ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
return new ArrayParser(createParser(element));
}
long id = v.getTypeId();
Type type = types.get(id);
if (type == null) {
throw new IOException("Type '" + v.getTypeName() + "' is not defined");
}
if (constantPool) {
ConstantMap pool = constantPools.get(id);
if (pool == null) {
pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
constantPools.put(id, pool);
}
return new ConstantMapValueParser(pool);
}
Parser parser = parsers.get(id);
if (parser == null) {
if (!v.getFields().isEmpty()) {
return createCompositeParser(type);
} else {
return registerParserType(type, createPrimitiveParser(type));
}
}
return parser;
}
private Parser createPrimitiveParser(Type type) throws IOException {
switch (type.getName()) {
case "int":
return new IntegerParser();
case "long":
return new LongParser();
case "float":
return new FloatParser();
case "double":
return new DoubleParser();
case "char":
return new CharacterParser();
case "boolean":
return new BooleanParser();
case "short":
return new ShortParser();
case "byte":
return new ByteParser();
case "java.lang.String":
ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
constantPools.put(type.getId(), pool);
return new StringParser(pool);
default:
throw new IOException("Unknown primitive type " + type.getName());
}
}
private Parser registerParserType(Type t, Parser parser) {
Parser p = parsers.get(t.getId());
// check if parser exists (known type)
if (p != null) {
return p;
}
parsers.put(t.getId(), parser);
return parser;
}
private CompositeParser createCompositeParser(Type type) throws IOException {
List<ValueDescriptor> vds = type.getFields();
Parser[] parsers = new Parser[vds.size()];
CompositeParser composite = new CompositeParser(parsers);
// need to pre-register so recursive types can be handled
registerParserType(type, composite);
int index = 0;
for (ValueDescriptor vd : vds) {
parsers[index++] = createParser(vd);
}
return composite;
}
private static final class BooleanParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
}
}
private static final class ByteParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Byte.valueOf(input.readByte());
}
}
private static final class LongParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Long.valueOf(input.readLong());
}
}
private static final class IntegerParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Integer.valueOf(input.readInt());
}
}
private static final class ShortParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Short.valueOf(input.readShort());
}
}
private static final class CharacterParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Character.valueOf(input.readChar());
}
}
private static final class FloatParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Float.valueOf(input.readFloat());
}
}
private static final class DoubleParser extends Parser {
@Override
public Object parse(RecordingInput input) throws IOException {
return Double.valueOf(input.readDouble());
}
}
private static final class StringParser extends Parser {
private final ConstantMap stringConstantMap;
private String last;
StringParser(ConstantMap stringConstantMap) {
this.stringConstantMap = stringConstantMap;
}
@Override
public Object parse(RecordingInput input) throws IOException {
String s = parseEncodedString(input);
if (!Objects.equals(s, last)) {
last = s;
}
return last;
}
private String parseEncodedString(RecordingInput input) throws IOException {
byte encoding = input.readByte();
if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) {
long id = input.readLong();
return (String) stringConstantMap.get(id);
} else {
return input.readEncodedString(encoding);
}
}
}
private final static class ArrayParser extends Parser {
private final Parser elementParser;
public ArrayParser(Parser elementParser) {
this.elementParser = elementParser;
}
@Override
public Object parse(RecordingInput input) throws IOException {
final int size = input.readInt();
input.require(size, "Array size %d exceeds available data" );
final Object[] array = new Object[size];
for (int i = 0; i < size; i++) {
array[i] = elementParser.parse(input);
}
return array;
}
}
private final static class CompositeParser extends Parser {
private final Parser[] parsers;
public CompositeParser(Parser[] valueParsers) {
this.parsers = valueParsers;
}
@Override
public Object parse(RecordingInput input) throws IOException {
final Object[] values = new Object[parsers.length];
for (int i = 0; i < values.length; i++) {
values[i] = parsers[i].parse(input);
}
return values;
}
}
private static final class ConstantMapValueParser extends Parser {
private final ConstantMap pool;
ConstantMapValueParser(ConstantMap pool) {
this.pool = pool;
}
@Override
public Object parse(RecordingInput input) throws IOException {
return pool.get(input.readLong());
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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.consumer;
import java.lang.reflect.Modifier;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded Java type, such as a class or an interface.
*
* @since 8
*/
public final class RecordedClass extends RecordedObject {
static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedClass>(type) {
@Override
RecordedClass createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedClass(desc, id, object, timeConverter);
}
};
}
private final long uniqueId;
// package private
private RecordedClass(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
super(descriptors, values, timeConverter);
this.uniqueId = id;
}
/**
* Returns the modifiers of the class.
* <p>
* See {@link java.lang.reflect.Modifier}
*
* @return the modifiers
*
* @see Modifier
*/
public int getModifiers() {
return getTyped("modifiers", Integer.class, -1);
}
/**
* Returns the class loader that defined the class.
* <P>
* If the bootstrap class loader is represented as {@code null} in the Java
* Virtual Machine (JVM), then {@code null} is also the return value of this method.
*
* @return the class loader defining this class, can be {@code null}
*/
public RecordedClassLoader getClassLoader() {
return getTyped("classLoader", RecordedClassLoader.class, null);
}
/**
* Returns the fully qualified name of the class (for example,
* {@code "java.lang.String"}).
*
* @return the class name, not {@code null}
*/
public String getName() {
return getTyped("name", String.class, null).replace("/", ".");
}
/**
* Returns a unique ID for the class.
* <p>
* The ID might not be the same between Java Virtual Machine (JVM) instances.
*
* @return a unique ID
*/
public long getId() {
return uniqueId;
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.consumer;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded Java class loader.
*
* @since 8
*/
public final class RecordedClassLoader extends RecordedObject {
static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedClassLoader>(type) {
@Override
RecordedClassLoader createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedClassLoader(desc, id, object, timeConverter);
}
};
}
private final long uniqueId;
// package private
private RecordedClassLoader(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
super(descriptors, values, timeConverter);
this.uniqueId = id;
}
/**
* Returns the class of the class loader.
* <P>
* If the bootstrap class loader is represented as {@code null} in the Java
* Virtual Machine (JVM), then {@code null} is also the return value of this
* method.
*
* @return class of the class loader, can be {@code null}
*/
public RecordedClass getType() {
return getTyped("type", RecordedClass.class, null);
}
/**
* Returns the name of the class loader (for example, "boot", "platform", and
* "app").
*
* @return the class loader name, can be {@code null}
*/
public String getName() {
return getTyped("name", String.class, null);
}
/**
* Returns a unique ID for the class loader.
* <p>
* The ID might not be the same between Java Virtual Machine (JVM) instances.
*
* @return a unique ID
*/
public long getId() {
return uniqueId;
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.consumer;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.EventInstrumentation;
/**
* A recorded event.
*
* @since 8
*/
public final class RecordedEvent extends RecordedObject {
private final EventType eventType;
private final long startTime;
// package private needed for efficient sorting
final long endTime;
// package private
RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
super(vds, values, timeConverter);
this.eventType = type;
this.startTime = startTime;
this.endTime = endTime;
}
/**
* Returns the stack trace that was created when the event was committed, or
* {@code null} if the event lacks a stack trace.
*
* @return stack trace, or {@code null} if doesn't exist for the event
*/
public RecordedStackTrace getStackTrace() {
return getTyped(EventInstrumentation.FIELD_STACK_TRACE, RecordedStackTrace.class, null);
}
/**
* Returns the thread from which the event was committed, or {@code null} if
* the thread was not recorded.
*
* @return thread, or {@code null} if doesn't exist for the event
*/
public RecordedThread getThread() {
return getTyped(EventInstrumentation.FIELD_EVENT_THREAD, RecordedThread.class, null);
}
/**
* Returns the event type that describes the event.
*
* @return the event type, not {@code null}
*/
public EventType getEventType() {
return eventType;
}
/**
* Returns the start time of the event.
* <p>
* If the event is an instant event, then the start time and end time are the same.
*
* @return the start time, not {@code null}
*/
public Instant getStartTime() {
return Instant.ofEpochSecond(0, startTime);
}
/**
* Returns the end time of the event.
* <p>
* If the event is an instant event, then the start time and end time are the same.
*
* @return the end time, not {@code null}
*/
public Instant getEndTime() {
return Instant.ofEpochSecond(0, endTime);
}
/**
* Returns the duration of the event, measured in nanoseconds.
*
* @return the duration in nanoseconds, not {@code null}
*/
public Duration getDuration() {
return Duration.ofNanos(endTime - startTime);
}
/**
* Returns the list of descriptors that describes the fields of the event.
*
* @return descriptors, not {@code null}
*/
@Override
public List<ValueDescriptor> getFields() {
return getEventType().getFields();
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.consumer;
import java.lang.reflect.Modifier;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded frame in a stack trace.
*
* @since 8
*/
public final class RecordedFrame extends RecordedObject {
static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedFrame>(type) {
@Override
RecordedFrame createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedFrame(desc, object, timeConverter);
}
};
}
// package private
RecordedFrame(List<ValueDescriptor> desc, Object[] objects, TimeConverter timeConverter) {
super(desc, objects, timeConverter);
}
/**
* Returns {@code true} if this is a Java frame, {@code false} otherwise.
* <p>
* A Java method that has a native modifier is considered a Java frame.
*
* @return {@code true} if this is a Java frame, {@code false} otherwise
*
* @see Modifier#isNative(int)
*/
public boolean isJavaFrame() {
// Only Java frames exist today, but this allows
// API to be extended for native frame in the future.
if (hasField("javaFrame")) {
return getTyped("javaFrame", Boolean.class, Boolean.TRUE);
}
return true;
}
/**
* Returns the bytecode index for the execution point that is represented by
* this recorded frame.
*
* @return byte code index, or {@code -1} if doesn't exist
*/
public int getBytecodeIndex() {
return getTyped("bytecodeIndex", Integer.class, Integer.valueOf(-1));
}
/**
* Returns the line number for the execution point that is represented by this
* recorded frame, or {@code -1} if doesn't exist
*
* @return the line number, or {@code -1} if doesn't exist
*/
public int getLineNumber() {
return getTyped("lineNumber", Integer.class, Integer.valueOf(-1));
}
/**
* Returns the frame type for the execution point that is represented by this
* recorded frame (for example, {@code "Interpreted"}, {@code "JIT compiled"} or
* {@code "Inlined"}).
*
* @return the frame type, or {@code null} if doesn't exist
*/
public String getType() {
return getTyped("type", String.class, null);
}
/**
* Returns the method for the execution point that is represented by this
* recorded frame.
*
* @return the method, not {@code null}
*/
public RecordedMethod getMethod() {
return getTyped("method", RecordedMethod.class, null);
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.consumer;
import java.lang.reflect.Modifier;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded method.
*
* @since 8
*/
public final class RecordedMethod extends RecordedObject {
static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedMethod>(type) {
@Override
RecordedMethod createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedMethod(desc, object, timeConverter);
}
};
}
private RecordedMethod(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
super(descriptors, objects, timeConverter);
}
/**
* Returns the class this method belongs to, if it belong to a Java frame.
* <p>
* To ensure this is a Java frame, use the {@link RecordedFrame#isJavaFrame()}
* method.
*
* @return the class, may be {@code null} if not a Java frame
*
* @see RecordedFrame#isJavaFrame()
*/
public RecordedClass getType() {
return getTyped("type", RecordedClass.class, null);
}
/**
* Returns the name of this method, for example {@code "toString"}.
* <p>
* If this method doesn't belong to a Java frame the result is undefined.
*
* @return method name, or {@code null} if doesn't exist
*
* @see RecordedFrame#isJavaFrame()
*/
public String getName() {
return getTyped("name", String.class, null);
}
/**
* Returns the method descriptor for this method (for example,
* {@code "(Ljava/lang/String;)V"}).
* <p>
* See Java Virtual Machine Specification, 4.3
* <p>
* If this method doesn't belong to a Java frame then the the result is undefined.
*
* @return method descriptor.
*
* @see RecordedFrame#isJavaFrame()
*/
public String getDescriptor() {
return getTyped("descriptor", String.class, null);
}
/**
* Returns the modifiers for this method.
* <p>
* If this method doesn't belong to a Java frame, then the result is undefined.
*
* @return the modifiers
*
* @see Modifier
* @see RecordedFrame#isJavaFrame
*/
public int getModifiers() {
return getTyped("modifiers", Integer.class, Integer.valueOf(0));
}
/**
* Returns whether this method is hidden (for example, wrapper code in a lambda
* expressions).
*
* @return {@code true} if method is hidden, {@code false} otherwise
*/
public boolean isHidden() {
return getTyped("hidden", Boolean.class, Boolean.FALSE);
}
}

View File

@@ -0,0 +1,903 @@
/*
* 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.consumer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Objects;
import jdk.jfr.Timespan;
import jdk.jfr.Timestamp;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.tool.PrettyWriter;
/**
* A complex data type that consists of one or more fields.
* <p>
* This class provides methods to select and query nested objects by passing a
* dot {@code "."} delimited {@code String} object (for instance,
* {@code "aaa.bbb"}). A method evaluates a nested object from left to right,
* and if a part is {@code null}, it throws {@code NullPointerException}.
*
* @since 8
*/
public class RecordedObject {
private final static class UnsignedValue {
private final Object o;
UnsignedValue(Object o) {
this.o = o;
}
Object value() {
return o;
}
}
private final Object[] objects;
private final List<ValueDescriptor> descriptors;
private final TimeConverter timeConverter;
// package private, not to be subclassed outside this package
RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
this.descriptors = descriptors;
this.objects = objects;
this.timeConverter = timeConverter;
}
// package private
final <T> T getTyped(String name, Class<T> clazz, T defaultValue) {
// Unnecessary to check field presence twice, but this
// will do for now.
if (!hasField(name)) {
return defaultValue;
}
T object = getValue(name);
if (object == null || object.getClass().isAssignableFrom(clazz)) {
return object;
} else {
return defaultValue;
}
}
/**
* Returns {@code true} if a field with the given name exists, {@code false}
* otherwise.
*
* @param name name of the field to get, not {@code null}
*
* @return {@code true} if the field exists, {@code false} otherwise.
*
* @see #getFields()
*/
public boolean hasField(String name) {
Objects.requireNonNull(name);
for (ValueDescriptor v : descriptors) {
if (v.getName().equals(name)) {
return true;
}
}
int dotIndex = name.indexOf(".");
if (dotIndex > 0) {
String structName = name.substring(0, dotIndex);
for (ValueDescriptor v : descriptors) {
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
RecordedObject child = getValue(structName);
if (child != null) {
return child.hasField(name.substring(dotIndex + 1));
}
}
}
}
return false;
}
/**
* Returns the value of the field with the given name.
* <p>
* The return type may be a primitive type or a subclass of
* {@link RecordedObject}.
* <p>
* It's possible to index into a nested object by using {@code "."} (for
* instance {@code "thread.group.parent.name}").
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
* <p>
* Example
*
* <pre>
* <code>
* if (event.hasField("intValue")) {
* int intValue = event.getValue("intValue");
* System.out.println("Int value: " + intValue);
* }
*
* if (event.hasField("objectClass")) {
* RecordedClass clazz = event.getValue("objectClass");
* System.out.println("Class name: " + clazz.getName());
* }
*
* if (event.hasField("sampledThread")) {
* RecordedThread sampledThread = event.getValue("sampledThread");
* System.out.println("Sampled thread: " + sampledThread.getName());
* }
* </code>
* </pre>
*
* @param <T> the return type
* @param name of the field to get, not {@code null}
* @throws IllegalArgumentException if no field called {@code name} exists
*
* @return the value, can be {@code null}
*
* @see #hasField(String)
*
*/
final public <T> T getValue(String name) {
@SuppressWarnings("unchecked")
T t = (T) getValue(name, false);
return t;
}
private Object getValue(String name, boolean allowUnsigned) {
Objects.requireNonNull(name);
int index = 0;
for (ValueDescriptor v : descriptors) {
if (name.equals(v.getName())) {
Object object = objects[index];
if (object == null) {
// error or missing
return null;
}
if (v.getFields().isEmpty()) {
if (allowUnsigned && PrivateAccess.getInstance().isUnsigned(v)) {
// Types that are meaningless to widen
if (object instanceof Character || object instanceof Long) {
return object;
}
return new UnsignedValue(object);
}
return object; // primitives and primitive arrays
} else {
if (object instanceof RecordedObject) {
// known types from factory
return object;
}
// must be array type
Object[] array = (Object[]) object;
if (v.isArray()) {
// struct array
return structifyArray(v, array, 0);
}
// struct
return new RecordedObject(v.getFields(), (Object[]) object, timeConverter);
}
}
index++;
}
int dotIndex = name.indexOf(".");
if (dotIndex > 0) {
String structName = name.substring(0, dotIndex);
for (ValueDescriptor v : descriptors) {
if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
RecordedObject child = getValue(structName);
String subName = name.substring(dotIndex + 1);
if (child != null) {
return child.getValue(subName, allowUnsigned);
} else {
// Call getValueDescriptor to trigger IllegalArgumentException if the name is
// incorrect. Type can't be validate due to type erasure
getValueDescriptor(v.getFields(), subName, null);
throw new NullPointerException("Field value for \"" + structName + "\" was null. Can't access nested field \"" + subName + "\"");
}
}
}
}
throw new IllegalArgumentException("Could not find field with name " + name);
}
// Returns the leaf value descriptor matches both name or value, or throws an
// IllegalArgumentException
private ValueDescriptor getValueDescriptor(List<ValueDescriptor> descriptors, String name, String leafType) {
int dotIndex = name.indexOf(".");
if (dotIndex > 0) {
String first = name.substring(0, dotIndex);
String second = name.substring(dotIndex + 1);
for (ValueDescriptor v : descriptors) {
if (v.getName().equals(first)) {
List<ValueDescriptor> fields = v.getFields();
if (!fields.isEmpty()) {
return getValueDescriptor(v.getFields(), second, leafType);
}
}
}
throw new IllegalArgumentException("Attempt to get unknown field \"" + first + "\"");
}
for (ValueDescriptor v : descriptors) {
if (v.getName().equals(name)) {
if (leafType != null && !v.getTypeName().equals(leafType)) {
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal data type conversion " + leafType);
}
return v;
}
}
throw new IllegalArgumentException("\"Attempt to get unknown field \"" + name + "\"");
}
// Gets a value, but checks that type and name is correct first
// This is to prevent a call to getString on a thread field, that is
// null to succeed.
private <T> T getTypedValue(String name, String typeName) {
Objects.requireNonNull(name);
// Validate name and type first
getValueDescriptor(descriptors, name, typeName);
return getValue(name);
}
private Object[] structifyArray(ValueDescriptor v, Object[] array, int dimension) {
if (array == null) {
return null;
}
Object[] structArray = new Object[array.length];
for (int i = 0; i < structArray.length; i++) {
Object arrayElement = array[i];
if (dimension == 0) {
// No general way to handle structarrays
// without invoking ObjectFactory for every instance (which may require id)
if (isStackFrameType(v.getTypeName())) {
structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter);
} else {
structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter);
}
} else {
structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
}
}
return structArray;
}
private boolean isStackFrameType(String typeName) {
if (ObjectFactory.STACK_FRAME_VERSION_1.equals(typeName)) {
return true;
}
if (ObjectFactory.STACK_FRAME_VERSION_2.equals(typeName)) {
return true;
}
return false;
}
/**
* Returns an immutable list of the fields for this object.
*
* @return the fields, not {@code null}
*/
public List<ValueDescriptor> getFields() {
return descriptors;
}
/**
* Returns the value of a field of type {@code boolean}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name name of the field to get, not {@code null}
*
* @return the value of the field, {@code true} or {@code false}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field is
* not of type {@code boolean}
*
* @see #hasField(String)
* @see #getValue(String)
*/
public final boolean getBoolean(String name) {
Object o = getValue(name);
if (o instanceof Boolean) {
return ((Boolean) o).booleanValue();
}
throw newIllegalArgumentException(name, "boolean");
}
/**
* Returns the value of a field of type {@code byte}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "foo.bar"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field
*
* @throws IllegalArgumentException if the field doesn't exist, or the field is
* not of type {@code byte}
*
* @see #hasField(String)
* @see #getValue(String)
*/
public final byte getByte(String name) {
Object o = getValue(name);
if (o instanceof Byte) {
return ((Byte) o).byteValue();
}
throw newIllegalArgumentException(name, "byte");
}
/**
* Returns the value of a field of type {@code char}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field as a {@code char}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field is
* not of type {@code char}
*
* @see #hasField(String)
* @see #getValue(String)
*/
public final char getChar(String name) {
Object o = getValue(name);
if (o instanceof Character) {
return ((Character) o).charValue();
}
throw newIllegalArgumentException(name, "char");
}
/**
* Returns the value of a field of type {@code short} or of another primitive
* type convertible to type {@code short} by a widening conversion.
* <p>
* This method can be used on the following types: {@code short} and {@code byte}.
* <p>
* If the field has the {@code @Unsigned} annotation and is of a narrower type
* than {@code short}, then the value is returned as an unsigned.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field converted to type {@code short}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to the type {@code short} by a widening
* conversion
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final short getShort(String name) {
Object o = getValue(name, true);
if (o instanceof Short) {
return ((Short) o).shortValue();
}
if (o instanceof Byte) {
return ((Byte) o).byteValue();
}
if (o instanceof UnsignedValue) {
Object u = ((UnsignedValue) o).value();
if (u instanceof Short) {
return ((Short) u).shortValue();
}
if (u instanceof Byte) {
return (short) Byte.toUnsignedInt(((Byte) u));
}
}
throw newIllegalArgumentException(name, "short");
}
/**
* Returns the value of a field of type {@code int} or of another primitive type
* that is convertible to type {@code int} by a widening conversion.
* <p>
* This method can be used on fields of the following types: {@code int},
* {@code short}, {@code char}, and {@code byte}.
* <p>
* If the field has the {@code @Unsigned} annotation and is of a narrower type
* than {@code int}, then the value will be returned as an unsigned.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field converted to type {@code int}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to the type {@code int} by a widening
* conversion
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final int getInt(String name) {
Object o = getValue(name, true);
if (o instanceof Integer) {
return ((Integer) o).intValue();
}
if (o instanceof Short) {
return ((Short) o).intValue();
}
if (o instanceof Character) {
return ((Character) o).charValue();
}
if (o instanceof Byte) {
return ((Byte) o).intValue();
}
if (o instanceof UnsignedValue) {
Object u = ((UnsignedValue) o).value();
if (u instanceof Integer) {
return ((Integer) u).intValue();
}
if (u instanceof Short) {
return Short.toUnsignedInt(((Short) u));
}
if (u instanceof Byte) {
return Byte.toUnsignedInt(((Byte) u));
}
}
throw newIllegalArgumentException(name, "int");
}
/**
* Returns the value of a field of type {@code float} or of another primitive
* type convertible to type {@code float} by a widening conversion.
* <p>
* This method can be used on fields of the following types: {@code float},
* {@code long}, {@code int}, {@code short}, {@code char}, and {@code byte}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field converted to type {@code float}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to the type {@code float} by a widening
* conversion
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final float getFloat(String name) {
Object o = getValue(name);
if (o instanceof Float) {
return ((Float) o).floatValue();
}
if (o instanceof Long) {
return ((Long) o).floatValue();
}
if (o instanceof Integer) {
return ((Integer) o).floatValue();
}
if (o instanceof Short) {
return ((Short) o).floatValue();
}
if (o instanceof Byte) {
return ((Byte) o).byteValue();
}
if (o instanceof Character) {
return ((Character) o).charValue();
}
throw newIllegalArgumentException(name, "float");
}
/**
* Returns the value of a field of type {@code long} or of another primitive
* type that is convertible to type {@code long} by a widening conversion.
* <p>
* This method can be used on fields of the following types: {@code long},
* {@code int}, {@code short}, {@code char}, and {@code byte}.
* <p>
* If the field has the {@code @Unsigned} annotation and is of a narrower type
* than {@code long}, then the value will be returned as an unsigned.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field converted to type {@code long}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to the type {@code long} via a widening
* conversion
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final long getLong(String name) {
Object o = getValue(name, true);
if (o instanceof Long) {
return ((Long) o).longValue();
}
if (o instanceof Integer) {
return ((Integer) o).longValue();
}
if (o instanceof Short) {
return ((Short) o).longValue();
}
if (o instanceof Character) {
return ((Character) o).charValue();
}
if (o instanceof Byte) {
return ((Byte) o).longValue();
}
if (o instanceof UnsignedValue) {
Object u = ((UnsignedValue) o).value();
if (u instanceof Integer) {
return Integer.toUnsignedLong(((Integer) u));
}
if (u instanceof Short) {
return Short.toUnsignedLong(((Short) u));
}
if (u instanceof Byte) {
return Byte.toUnsignedLong(((Byte) u));
}
}
throw newIllegalArgumentException(name, "long");
}
/**
* Returns the value of a field of type {@code double} or of another primitive
* type that is convertible to type {@code double} by a widening conversion.
* <p>
* This method can be used on fields of the following types: {@code double}, {@code float},
* {@code long}, {@code int}, {@code short}, {@code char}, and {@code byte}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field converted to type {@code double}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to the type {@code double} by a widening
* conversion
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final double getDouble(String name) {
Object o = getValue(name);
if (o instanceof Double) {
return ((Double) o).doubleValue();
}
if (o instanceof Float) {
return ((Float) o).doubleValue();
}
if (o instanceof Long) {
return ((Long) o).doubleValue();
}
if (o instanceof Integer) {
return ((Integer) o).doubleValue();
}
if (o instanceof Short) {
return ((Short) o).doubleValue();
}
if (o instanceof Byte) {
return ((Byte) o).byteValue();
}
if (o instanceof Character) {
return ((Character) o).charValue();
}
throw newIllegalArgumentException(name, "double");
}
/**
* Returns the value of a field of type {@code String}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "foo.bar"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field as a {@code String}, can be {@code null}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* isn't of type {@code String}
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final String getString(String name) {
return getTypedValue(name, "java.lang.String");
}
/**
* Returns the value of a timespan field.
* <p>
* This method can be used on fields annotated with {@code @Timespan}, and of
* the following types: {@code long}, {@code int}, {@code short}, {@code char},
* and {@code byte}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return a time span represented as a {@code Duration}, not {@code null}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to a {@code Duration} object
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final Duration getDuration(String name) {
Object o = getValue(name);
if (o instanceof Long) {
return getDuration(((Long) o).longValue(), name);
}
if (o instanceof Integer) {
return getDuration(((Integer) o).longValue(), name);
}
if (o instanceof Short) {
return getDuration(((Short) o).longValue(), name);
}
if (o instanceof Character) {
return getDuration(((Character) o).charValue(), name);
}
if (o instanceof Byte) {
return getDuration(((Byte) o).longValue(), name);
}
if (o instanceof UnsignedValue) {
Object u = ((UnsignedValue) o).value();
if (u instanceof Integer) {
return getDuration(Integer.toUnsignedLong((Integer) u), name);
}
if (u instanceof Short) {
return getDuration(Short.toUnsignedLong((Short) u), name);
}
if (u instanceof Byte) {
return getDuration(Short.toUnsignedLong((Byte) u), name);
}
}
throw newIllegalArgumentException(name, "java,time.Duration");
}
private Duration getDuration(long timespan, String name) throws InternalError {
ValueDescriptor v = getValueDescriptor(descriptors, name, null);
if (timespan == Long.MIN_VALUE) {
return Duration.ofSeconds(Long.MIN_VALUE, 0);
}
Timespan ts = v.getAnnotation(Timespan.class);
if (ts != null) {
switch (ts.value()) {
case Timespan.MICROSECONDS:
return Duration.ofNanos(1000 * timespan);
case Timespan.SECONDS:
return Duration.ofSeconds(timespan);
case Timespan.MILLISECONDS:
return Duration.ofMillis(timespan);
case Timespan.NANOSECONDS:
return Duration.ofNanos(timespan);
case Timespan.TICKS:
return Duration.ofNanos(timeConverter.convertTimespan(timespan));
}
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
}
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timespan");
}
/**
* Returns the value of a timestamp field.
* <p>
* This method can be used on fields annotated with {@code @Timestamp}, and of
* the following types: {@code long}, {@code int}, {@code short}, {@code char}
* and {@code byte}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return a timstamp represented as an {@code Instant}, not {@code null}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* value can't be converted to an {@code Instant} object
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final Instant getInstant(String name) {
Object o = getValue(name, true);
if (o instanceof Long) {
return getInstant(((Long) o).longValue(), name);
}
if (o instanceof Integer) {
return getInstant(((Integer) o).longValue(), name);
}
if (o instanceof Short) {
return getInstant(((Short) o).longValue(), name);
}
if (o instanceof Character) {
return getInstant(((Character) o).charValue(), name);
}
if (o instanceof Byte) {
return getInstant(((Byte) o).longValue(), name);
}
if (o instanceof UnsignedValue) {
Object u = ((UnsignedValue) o).value();
if (u instanceof Integer) {
return getInstant(Integer.toUnsignedLong((Integer) u), name);
}
if (u instanceof Short) {
return getInstant(Short.toUnsignedLong((Short) u), name);
}
if (u instanceof Byte) {
return getInstant(Short.toUnsignedLong((Byte) u), name);
}
}
throw newIllegalArgumentException(name, "java.time.Instant");
}
private Instant getInstant(long timestamp, String name) {
ValueDescriptor v = getValueDescriptor(descriptors, name, null);
Timestamp ts = v.getAnnotation(Timestamp.class);
if (ts != null) {
if (timestamp == Long.MIN_VALUE) {
return Instant.MIN;
}
switch (ts.value()) {
case Timestamp.MILLISECONDS_SINCE_EPOCH:
return Instant.ofEpochMilli(timestamp);
case Timestamp.TICKS:
return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp));
}
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
}
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timestamp");
}
/**
* Returns the value of a field of type {@code Class}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "aaa.bbb"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field as a {@code RecordedClass}, can be
* {@code null}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* isn't of type {@code Class}
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final RecordedClass getClass(String name) {
return getTypedValue(name, "java.lang.Class");
}
/**
* Returns the value of a field of type {@code Thread}.
* <p>
* It's possible to index into a nested object using {@code "."} (for example,
* {@code "foo.bar"}).
* <p>
* A field might change or be removed in a future JDK release. A best practice
* for callers of this method is to validate the field before attempting access.
*
* @param name of the field to get, not {@code null}
*
* @return the value of the field as a {@code RecordedThread} object, can be
* {@code null}
*
* @throws IllegalArgumentException if the field doesn't exist, or the field
* isn't of type {@code Thread}
*
* @see #hasField(String)
* @set #getValue(String)
*/
public final RecordedThread getThread(String name) {
return getTypedValue(name, "java.lang.Thread");
}
/**
* Returns a textual representation of this object.
*
* @return textual description of this object
*/
@Override
final public String toString() {
StringWriter s = new StringWriter();
PrettyWriter p = new PrettyWriter(new PrintWriter(s));
p.setStackDepth(5);
if (this instanceof RecordedEvent) {
p.print((RecordedEvent) this);
} else {
p.print(this, "");
}
p.flush(true);
return s.toString();
}
// package private for now. Used by EventWriter
OffsetDateTime getOffsetDateTime(String name) {
Instant instant = getInstant(name);
if (instant.equals(Instant.MIN)) {
return OffsetDateTime.MIN;
}
return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset());
}
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName);
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.consumer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded stack trace.
*
* @since 8
*/
public final class RecordedStackTrace extends RecordedObject {
static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedStackTrace>(type) {
@Override
RecordedStackTrace createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedStackTrace(desc, object, timeConverter);
}
};
}
private RecordedStackTrace(List<ValueDescriptor> desc, Object[] values, TimeConverter timeConverter) {
super(desc, values, timeConverter);
}
/**
* Returns the frames in the stack trace.
*
* @return a list of Java stack frames, not {@code null}
*/
@SuppressWarnings("unchecked")
public List<RecordedFrame> getFrames() {
Object[] array = getTyped("frames", Object[].class, null);
if (array == null) {
return Collections.EMPTY_LIST;
}
List<?> list = Arrays.asList(array);
return (List<RecordedFrame>) list;
}
/**
* Returns {@code true} if the stack trace is truncated due to its size,
* {@code false} otherwise.
*
* @return {@code true} if the stack trace is truncated, {@code false}
* otherwise
*/
public boolean isTruncated() {
return getTyped("truncated", Boolean.class, true);
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.consumer;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded thread.
*
* @since 8
*/
public final class RecordedThread extends RecordedObject {
static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedThread>(type) {
@Override
RecordedThread createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedThread(desc, id, object, timeConverter);
}
};
}
private final long uniqueId;
private RecordedThread(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
super(descriptors, values, timeConverter);
this.uniqueId = id;
}
/**
* Returns the thread name used by the operating system.
*
* @return the OS thread name, or {@code null} if doesn't exist
*/
public String getOSName() {
return getTyped("osName", String.class, null);
}
/**
* Returns the thread ID used by the operating system.
*
* @return The Java thread ID, or {@code -1} if doesn't exist
*/
public long getOSThreadId() {
Long l = getTyped("osThreadId", Long.class, -1L);
return l.longValue();
}
/**
* Returns the Java thread group, if available.
*
* @return the thread group, or {@code null} if doesn't exist
*/
public RecordedThreadGroup getThreadGroup() {
return getTyped("group", RecordedThreadGroup.class, null);
}
/**
* Returns the Java thread name, or {@code null} if if doesn't exist.
* <p>
* Returns {@code java.lang.Thread.getName()} if the thread has a Java
* representation. {@code null} otherwise.
*
* @return the Java thread name, or {@code null} if doesn't exist
*/
public String getJavaName() {
return getTyped("javaName", String.class, null);
}
/**
* Returns the Java thread ID, or {@code -1} if it's not a Java thread.
*
* @return the Java thread ID, or {@code -1} if it's not a Java thread
*/
public long getJavaThreadId() {
Long l = getTyped("javaThreadId", Long.class, -1L);
return l.longValue();
}
/**
* Returns a unique ID for both native threads and Java threads that can't be
* reused within the lifespan of the JVM.
* <p>
* See {@link #getJavaThreadId()} for the ID that is returned by
* {@code java.lang.Thread.getId()}
*
* @return a unique ID for the thread
*/
public long getId() {
return uniqueId;
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.consumer;
import java.util.List;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/**
* A recorded Java thread group.
*
* @since 8
*/
public final class RecordedThreadGroup extends RecordedObject {
static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedThreadGroup>(type) {
@Override
RecordedThreadGroup createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedThreadGroup(desc, object, timeConverter);
}
};
}
private RecordedThreadGroup(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
super(descriptors, objects, timeConverter);
}
/**
* Returns the name of the thread group, or {@code null} if doesn't exist.
*
* @return the thread group name, or {@code null} if doesn't exist
*/
public String getName() {
return getTyped("name", String.class, null);
}
/**
* Returns the parent thread group, or {@code null} if it doesn't exist.
*
* @return parent thread group, or {@code null} if it doesn't exist.
*/
public RecordedThreadGroup getParent() {
return getTyped("parent", RecordedThreadGroup.class, null);
}
}

View File

@@ -0,0 +1,269 @@
/*
* 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.consumer;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
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;
import jdk.jfr.internal.consumer.RecordingInternals;
/**
* A recording file.
* <p>
* The following example shows how read and print all events in a recording file.
*
* <pre>
* <code>
* try (RecordingFile recordingFile = new RecordingFile(Paths.get("recording.jfr"))) {
* while (recordingFile.hasMoreEvents()) {
* RecordedEvent event = recordingFile.readEvent();
* System.out.println(event);
* }
* }
* </code>
* </pre>
*
* @since 8
*/
public final class RecordingFile implements Closeable {
static{
RecordingInternals.INSTANCE = new RecordingInternals() {
public List<Type> readTypes(RecordingFile file) throws IOException {
return file.readTypes();
}
public boolean isLastEventInChunk(RecordingFile file) {
return file.isLastEventInChunk;
}
@Override
public Object getOffsetDataTime(RecordedObject event, String name) {
return event.getOffsetDateTime(name);
}
@Override
public void sort(List<RecordedEvent> events) {
Collections.sort(events, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
}
};
}
private boolean isLastEventInChunk;
private final File file;
private RecordingInput input;
private ChunkParser chunkParser;
private RecordedEvent nextEvent;
private boolean eof;
/**
* Creates a recording file.
*
* @param file the path of the file to open, not {@code null}
* @throws IOException if it's not a valid recording file, or an I/O error
* occurred
* @throws NoSuchFileException if the {@code file} can't be located
*
* @throws SecurityException if a security manager exists and its
* {@code checkRead} method denies read access to the file.
*/
public RecordingFile(Path file) throws IOException {
this.file = file.toFile();
this.input = new RecordingInput(this.file);
findNext();
}
/**
* Reads the next event in the recording.
*
* @return the next event, not {@code null}
*
* @throws EOFException if no more events exist in the recording file
* @throws IOException if an I/O error occurs.
*
* @see #hasMoreEvents()
*/
public RecordedEvent readEvent() throws IOException {
if (eof) {
ensureOpen();
throw new EOFException();
}
isLastEventInChunk = false;
RecordedEvent event = nextEvent;
nextEvent = chunkParser.readEvent();
if (nextEvent == null) {
isLastEventInChunk = true;
findNext();
}
return event;
}
/**
* Returns {@code true} if unread events exist in the recording file,
* {@code false} otherwise.
*
* @return {@code true} if unread events exist in the recording, {@code false}
* otherwise.
*/
public boolean hasMoreEvents() {
return !eof;
}
/**
* Returns a list of all event types in this recording.
*
* @return a list of event types, not {@code null}
* @throws IOException if an I/O error occurred while reading from the file
*
* @see #hasMoreEvents()
*/
public List<EventType> readEventTypes() throws IOException {
ensureOpen();
List<EventType> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file)) {
ChunkHeader ch = new ChunkHeader(ri);
aggregateEventTypeForChunk(ch, types, foundIds);
while (!ch.isLastChunk()) {
ch = ch.nextHeader();
aggregateEventTypeForChunk(ch, types, foundIds);
}
}
return types;
}
List<Type> readTypes() throws IOException {
ensureOpen();
List<Type> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file)) {
ChunkHeader ch = new ChunkHeader(ri);
aggregateTypeForChunk(ch, types, foundIds);
while (!ch.isLastChunk()) {
ch = ch.nextHeader();
aggregateTypeForChunk(ch, types, foundIds);
}
}
return types;
}
private void aggregateTypeForChunk(ChunkHeader ch, List<Type> types, HashSet<Long> foundIds) throws IOException {
MetadataDescriptor m = ch.readMetadata();
for (Type t : m.getTypes()) {
if (!foundIds.contains(t.getId())) {
types.add(t);
foundIds.add(t.getId());
}
}
}
private static void aggregateEventTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException {
MetadataDescriptor m = ch.readMetadata();
for (EventType t : m.getEventTypes()) {
if (!foundIds.contains(t.getId())) {
types.add(t);
foundIds.add(t.getId());
}
}
}
/**
* Closes this recording file and releases any system resources that are
* associated with it.
*
* @throws IOException if an I/O error occurred
*/
public void close() throws IOException {
if (input != null) {
eof = true;
input.close();
chunkParser = null;
input = null;
nextEvent = null;
}
}
/**
* Returns a list of all events in a file.
* <p>
* This method is intended for simple cases where it's convenient to read all
* events in a single operation. It isn't intended for reading large files.
*
* @param path the path to the file, not {@code null}
*
* @return the events from the file as a {@code List} object; whether the
* {@code List} is modifiable or not is implementation dependent and
* therefore not specified, not {@code null}
*
* @throws IOException if an I/O error occurred, it's not a Flight Recorder
* file or a version of a JFR file that can't be parsed
*
* @throws SecurityException if a security manager exists and its
* {@code checkRead} method denies read access to the file.
*/
public static List<RecordedEvent> readAllEvents(Path path) throws IOException {
try (RecordingFile r = new RecordingFile(path)) {
List<RecordedEvent> list = new ArrayList<>();
while (r.hasMoreEvents()) {
list.add(r.readEvent());
}
return list;
}
}
// either sets next to an event or sets eof to true
private void findNext() throws IOException {
while (nextEvent == null) {
if (chunkParser == null) {
chunkParser = new ChunkParser(input);
} else if (!chunkParser.isLastChunk()) {
chunkParser = chunkParser.nextChunkParser();
} else {
eof = true;
return;
}
nextEvent = chunkParser.readEvent();
}
}
private void ensureOpen() throws IOException {
if (input == null) {
throw new IOException("Stream Closed");
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.consumer;
import java.time.DateTimeException;
import java.time.ZoneOffset;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.consumer.ChunkHeader;
/**
* Converts ticks to nanoseconds
*/
final class TimeConverter {
private final long startTicks;
private final long startNanos;
private final double divisor;
private final ZoneOffset zoneOffet;
TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
this.startTicks = chunkHeader.getStartTicks();
this.startNanos = chunkHeader.getStartNanos();
this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
this.zoneOffet = zoneOfSet(rawOffset);
}
private ZoneOffset zoneOfSet(int rawOffset) {
try {
return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
} catch (DateTimeException dte) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
}
return ZoneOffset.UTC;
}
public long convertTimestamp(long ticks) {
return startNanos + (long) ((ticks - startTicks) / divisor);
}
public long convertTimespan(long ticks) {
return (long) (ticks / divisor);
}
public ZoneOffset getZoneOffset() {
return zoneOffet;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2017, 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 consuming Flight Recorder data.
* <p>
* In the following example, the program prints a histogram of all method samples in a recording.
* <pre>
* <code>
* public static void main(String[] args) {
* if (args.length != 0) {
* System.out.println("Must specify recording file.");
* return;
* }
* try (RecordingFile f = new RecordingFile(Paths.get(args[0]))) {
* Map{@literal <}String, SimpleEntry{@literal <}String, Integer{@literal >}{@literal >} histogram = new HashMap{@literal <}{@literal >}();
* int total = 0;
* while (f.hasMoreEvents()) {
* RecordedEvent event = f.readEvent();
* if (event.getEventType().getName().equals("jdk.ExecutionSample")) {
* RecordedStackTrace s = event.getStackTrace();
* if (s != null) {
* RecordedFrame topFrame= s.getFrames().get(0);
* if (topFrame.isJavaFrame()) {
* RecordedMethod method = topFrame.getMethod();
* String methodName = method.getType().getName() + "#" + method.getName() + " " + method.getDescriptor();
* Entry entry = histogram.computeIfAbsent(methodName, u -{@literal >} new SimpleEntry{@literal <}String, Integer{@literal >}(methodName, 0));
* entry.setValue(entry.getValue() + 1);
* total++;
* }
* }
* }
* }
* List{@literal <}SimpleEntry{@literal <}String, Integer{@literal >}{@literal >} entries = new ArrayList{@literal <}{@literal >}(histogram.values());
* entries.sort((u, v) -{@literal >} v.getValue().compareTo(u.getValue()));
* for (SimpleEntry{@literal <}String, Integer{@literal >} c : entries) {
* System.out.printf("%2.0f%% %s\n", 100 * (float) c.getValue() / total, c.getKey());
* }
* System.out.println("\nSample count: " + total);
* } catch (IOException ioe) {
* System.out.println("Error reading file " + args[0] + ". " + ioe.getMessage());
* }
* }
* </code>
* </pre>
* <p>
* <b>Null-handling</b>
* <p>
* All methods define whether they accept or return {@code null} in the Javadoc.
* Typically this is expressed as {@code "not null"}. If a {@code null}
* parameter is used where it is not allowed, a
* {@code java.lang.NullPointerException} is thrown. If a {@code null}
* parameters is passed to a method that throws other exceptions, such as
* {@code java.io.IOException}, the {@code java.lang.NullPointerException} takes
* precedence, unless the Javadoc for the method explicitly states how
* {@code null} is handled, i.e. by throwing {@code java.lang.IllegalArgumentException}.
*
* @since 8
*/
package jdk.jfr.consumer;