8205516: JFR tool
Reviewed-by: mgronlun
This commit is contained in:
parent
a4d8574130
commit
afdb850f87
31
make/launcher/Launcher-jdk.jfr.gmk
Normal file
31
make/launcher/Launcher-jdk.jfr.gmk
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
#
|
||||||
|
# This code is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License version 2 only, as
|
||||||
|
# published by the Free Software Foundation. Oracle designates this
|
||||||
|
# particular file as subject to the "Classpath" exception as provided
|
||||||
|
# by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
#
|
||||||
|
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
# version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
# accompanied this code).
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License version
|
||||||
|
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
# or visit www.oracle.com if you need additional information or have any
|
||||||
|
# questions.
|
||||||
|
#
|
||||||
|
|
||||||
|
include LauncherCommon.gmk
|
||||||
|
|
||||||
|
$(eval $(call SetupBuildLauncher, jfr, \
|
||||||
|
MAIN_CLASS := jdk.jfr.internal.tool.Main, \
|
||||||
|
CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS, \
|
||||||
|
))
|
@ -158,12 +158,14 @@ int EmitEventOperation::write_events(EdgeStore* edge_store) {
|
|||||||
const jlong last_sweep = _emit_all ? max_jlong : _object_sampler->last_sweep().value();
|
const jlong last_sweep = _emit_all ? max_jlong : _object_sampler->last_sweep().value();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < _object_sampler->item_count(); ++i) {
|
const ObjectSample* current = _object_sampler->first();
|
||||||
const ObjectSample* sample = _object_sampler->item_at(i);
|
while (current != NULL) {
|
||||||
if (sample->is_alive_and_older_than(last_sweep)) {
|
ObjectSample* prev = current->prev();
|
||||||
write_event(sample, edge_store);
|
if (current->is_alive_and_older_than(last_sweep)) {
|
||||||
|
write_event(current, edge_store);
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
current = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore thread local stack trace and thread id
|
// restore thread local stack trace and thread id
|
||||||
|
@ -122,6 +122,10 @@ const ObjectSample* ObjectSampler::last() const {
|
|||||||
return _list->last();
|
return _list->last();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ObjectSample* ObjectSampler::first() const {
|
||||||
|
return _list->first();
|
||||||
|
}
|
||||||
|
|
||||||
const ObjectSample* ObjectSampler::last_resolved() const {
|
const ObjectSample* ObjectSampler::last_resolved() const {
|
||||||
return _list->last_resolved();
|
return _list->last_resolved();
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ class ObjectSampler : public CHeapObj<mtTracing> {
|
|||||||
const ObjectSample* item_at(int index) const;
|
const ObjectSample* item_at(int index) const;
|
||||||
ObjectSample* item_at(int index);
|
ObjectSample* item_at(int index);
|
||||||
int item_count() const;
|
int item_count() const;
|
||||||
|
const ObjectSample* first() const;
|
||||||
const ObjectSample* last() const;
|
const ObjectSample* last() const;
|
||||||
const ObjectSample* last_resolved() const;
|
const ObjectSample* last_resolved() const;
|
||||||
void set_last_resolved(const ObjectSample* sample);
|
void set_last_resolved(const ObjectSample* sample);
|
||||||
|
@ -45,6 +45,10 @@ ObjectSample* SampleList::last() const {
|
|||||||
return _in_use_list.head();
|
return _in_use_list.head();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectSample* SampleList::first() const {
|
||||||
|
return _in_use_list.tail();
|
||||||
|
}
|
||||||
|
|
||||||
const ObjectSample* SampleList::last_resolved() const {
|
const ObjectSample* SampleList::last_resolved() const {
|
||||||
return _last_resolved;
|
return _last_resolved;
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ class SampleList : public JfrCHeapObj {
|
|||||||
void set_last_resolved(const ObjectSample* sample);
|
void set_last_resolved(const ObjectSample* sample);
|
||||||
ObjectSample* get();
|
ObjectSample* get();
|
||||||
ObjectSample* last() const;
|
ObjectSample* last() const;
|
||||||
|
ObjectSample* first() const;
|
||||||
void release(ObjectSample* sample);
|
void release(ObjectSample* sample);
|
||||||
const ObjectSample* last_resolved() const;
|
const ObjectSample* last_resolved() const;
|
||||||
ObjectSample* reuse(ObjectSample* sample);
|
ObjectSample* reuse(ObjectSample* sample);
|
||||||
|
@ -291,7 +291,7 @@ public final class ValueDescriptor {
|
|||||||
if (type.isSimpleType()) {
|
if (type.isSimpleType()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(type.getFields());
|
return type.getFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
|
@ -61,7 +61,7 @@ final class ChunkParser {
|
|||||||
this.chunkHeader = header;
|
this.chunkHeader = header;
|
||||||
this.metadata = header.readMetadata();
|
this.metadata = header.readMetadata();
|
||||||
this.absoluteChunkEnd = header.getEnd();
|
this.absoluteChunkEnd = header.getEnd();
|
||||||
this.timeConverter = new TimeConverter(chunkHeader);
|
this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
|
||||||
|
|
||||||
ParserFactory factory = new ParserFactory(metadata, timeConverter);
|
ParserFactory factory = new ParserFactory(metadata, timeConverter);
|
||||||
LongMap<ConstantMap> constantPools = factory.getConstantPools();
|
LongMap<ConstantMap> constantPools = factory.getConstantPools();
|
||||||
@ -114,9 +114,7 @@ final class ChunkParser {
|
|||||||
boolean flush = input.readBoolean();
|
boolean flush = input.readBoolean();
|
||||||
int poolCount = input.readInt();
|
int poolCount = input.readInt();
|
||||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
|
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
|
||||||
return "New constant pool: startPosition=" + position +
|
return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
|
||||||
", size=" + size + ", deltaToNext=" + delta +
|
|
||||||
", flush=" + flush + ", poolCount=" + poolCount;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0; i < poolCount; i++) {
|
for (int i = 0; i < poolCount; i++) {
|
||||||
|
@ -41,7 +41,8 @@ import jdk.jfr.internal.EventInstrumentation;
|
|||||||
public final class RecordedEvent extends RecordedObject {
|
public final class RecordedEvent extends RecordedObject {
|
||||||
private final EventType eventType;
|
private final EventType eventType;
|
||||||
private final long startTime;
|
private final long startTime;
|
||||||
private final long endTime;
|
// package private needed for efficient sorting
|
||||||
|
final long endTime;
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
|
RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
|
||||||
|
@ -25,11 +25,11 @@
|
|||||||
|
|
||||||
package jdk.jfr.consumer;
|
package jdk.jfr.consumer;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ import jdk.jfr.Timespan;
|
|||||||
import jdk.jfr.Timestamp;
|
import jdk.jfr.Timestamp;
|
||||||
import jdk.jfr.ValueDescriptor;
|
import jdk.jfr.ValueDescriptor;
|
||||||
import jdk.jfr.internal.PrivateAccess;
|
import jdk.jfr.internal.PrivateAccess;
|
||||||
import jdk.jfr.internal.cmd.PrettyWriter;
|
import jdk.jfr.internal.tool.PrettyWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A complex data type that consists of one or more fields.
|
* A complex data type that consists of one or more fields.
|
||||||
@ -872,20 +872,21 @@ public class RecordedObject {
|
|||||||
final public String toString() {
|
final public String toString() {
|
||||||
StringWriter s = new StringWriter();
|
StringWriter s = new StringWriter();
|
||||||
PrettyWriter p = new PrettyWriter(new PrintWriter(s));
|
PrettyWriter p = new PrettyWriter(new PrintWriter(s));
|
||||||
try {
|
p.setStackDepth(5);
|
||||||
if (this instanceof RecordedEvent) {
|
if (this instanceof RecordedEvent) {
|
||||||
p.print((RecordedEvent) this);
|
p.print((RecordedEvent) this);
|
||||||
} else {
|
} else {
|
||||||
p.print(this, "");
|
p.print(this, "");
|
||||||
}
|
}
|
||||||
|
p.flush(true);
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore, should not happen with StringWriter
|
|
||||||
}
|
|
||||||
p.flush();
|
|
||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// package private for now. Used by EventWriter
|
||||||
|
OffsetDateTime getOffsetDateTime(String name) {
|
||||||
|
return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset());
|
||||||
|
}
|
||||||
|
|
||||||
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
|
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
|
||||||
return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName);
|
return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName);
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,16 @@ import java.io.IOException;
|
|||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jdk.jfr.EventType;
|
import jdk.jfr.EventType;
|
||||||
import jdk.jfr.internal.MetadataDescriptor;
|
import jdk.jfr.internal.MetadataDescriptor;
|
||||||
|
import jdk.jfr.internal.Type;
|
||||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||||
import jdk.jfr.internal.consumer.RecordingInput;
|
import jdk.jfr.internal.consumer.RecordingInput;
|
||||||
|
import jdk.jfr.internal.consumer.RecordingInternals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A recording file.
|
* A recording file.
|
||||||
@ -59,7 +62,29 @@ import jdk.jfr.internal.consumer.RecordingInput;
|
|||||||
* @since 9
|
* @since 9
|
||||||
*/
|
*/
|
||||||
public final class RecordingFile implements Closeable {
|
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 final File file;
|
||||||
private RecordingInput input;
|
private RecordingInput input;
|
||||||
private ChunkParser chunkParser;
|
private ChunkParser chunkParser;
|
||||||
@ -98,9 +123,11 @@ public final class RecordingFile implements Closeable {
|
|||||||
ensureOpen();
|
ensureOpen();
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
isLastEventInChunk = false;
|
||||||
RecordedEvent event = nextEvent;
|
RecordedEvent event = nextEvent;
|
||||||
nextEvent = chunkParser.readEvent();
|
nextEvent = chunkParser.readEvent();
|
||||||
if (nextEvent == null) {
|
if (nextEvent == null) {
|
||||||
|
isLastEventInChunk = true;
|
||||||
findNext();
|
findNext();
|
||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
@ -129,6 +156,21 @@ public final class RecordingFile implements Closeable {
|
|||||||
ensureOpen();
|
ensureOpen();
|
||||||
List<EventType> types = new ArrayList<>();
|
List<EventType> types = new ArrayList<>();
|
||||||
HashSet<Long> foundIds = new HashSet<>();
|
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)) {
|
try (RecordingInput ri = new RecordingInput(file)) {
|
||||||
ChunkHeader ch = new ChunkHeader(ri);
|
ChunkHeader ch = new ChunkHeader(ri);
|
||||||
aggregateTypeForChunk(ch, types, foundIds);
|
aggregateTypeForChunk(ch, types, foundIds);
|
||||||
@ -140,7 +182,17 @@ public final class RecordingFile implements Closeable {
|
|||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void aggregateTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException {
|
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();
|
MetadataDescriptor m = ch.readMetadata();
|
||||||
for (EventType t : m.getEventTypes()) {
|
for (EventType t : m.getEventTypes()) {
|
||||||
if (!foundIds.contains(t.getId())) {
|
if (!foundIds.contains(t.getId())) {
|
||||||
|
@ -25,6 +25,12 @@
|
|||||||
|
|
||||||
package jdk.jfr.consumer;
|
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;
|
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,11 +40,22 @@ final class TimeConverter {
|
|||||||
private final long startTicks;
|
private final long startTicks;
|
||||||
private final long startNanos;
|
private final long startNanos;
|
||||||
private final double divisor;
|
private final double divisor;
|
||||||
|
private final ZoneOffset zoneOffet;
|
||||||
|
|
||||||
TimeConverter(ChunkHeader chunkHeader) {
|
TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
|
||||||
this.startTicks = chunkHeader.getStartTicks();
|
this.startTicks = chunkHeader.getStartTicks();
|
||||||
this.startNanos = chunkHeader.getStartNanos();
|
this.startNanos = chunkHeader.getStartNanos();
|
||||||
this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
|
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) {
|
public long convertTimestamp(long ticks) {
|
||||||
@ -48,4 +65,8 @@ final class TimeConverter {
|
|||||||
public long convertTimespan(long ticks) {
|
public long convertTimespan(long ticks) {
|
||||||
return (long) (ticks / divisor);
|
return (long) (ticks / divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ZoneOffset getZoneOffset() {
|
||||||
|
return zoneOffet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ public final class OldObjectSample {
|
|||||||
private static final String OLD_OBJECT_CUTOFF = EVENT_NAME + "#" + Cutoff.NAME;
|
private static final String OLD_OBJECT_CUTOFF = EVENT_NAME + "#" + Cutoff.NAME;
|
||||||
private static final String OLD_OBJECT_ENABLED = EVENT_NAME + "#" + Enabled.NAME;
|
private static final String OLD_OBJECT_ENABLED = EVENT_NAME + "#" + Enabled.NAME;
|
||||||
|
|
||||||
// Emit if old object is enabled in recoding with cutoff for that recording
|
// Emit if old object is enabled in recording with cutoff for that recording
|
||||||
public static void emit(PlatformRecording recording) {
|
public static void emit(PlatformRecording recording) {
|
||||||
if (isEnabled(recording)) {
|
if (isEnabled(recording)) {
|
||||||
long nanos = CutoffSetting.parseValueSafe(recording.getSettings().get(OLD_OBJECT_CUTOFF));
|
long nanos = CutoffSetting.parseValueSafe(recording.getSettings().get(OLD_OBJECT_CUTOFF));
|
||||||
@ -59,7 +59,7 @@ public final class OldObjectSample {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Emit if old object is enabled for at least one recording, and use the largest
|
// Emit if old object is enabled for at least one recording, and use the largest
|
||||||
// cutoff for an enabled recoding
|
// cutoff for an enabled recording
|
||||||
public static void emit(List<PlatformRecording> recordings, Boolean pathToGcRoots) {
|
public static void emit(List<PlatformRecording> recordings, Boolean pathToGcRoots) {
|
||||||
boolean enabled = false;
|
boolean enabled = false;
|
||||||
long cutoffNanos = Boolean.TRUE.equals(pathToGcRoots) ? Long.MAX_VALUE : 0L;
|
long cutoffNanos = Boolean.TRUE.equals(pathToGcRoots) ? Long.MAX_VALUE : 0L;
|
||||||
|
@ -27,6 +27,7 @@ package jdk.jfr.internal;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -71,7 +72,7 @@ public class Type implements Comparable<Type> {
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final String superType;
|
private final String superType;
|
||||||
private final boolean constantPool;
|
private final boolean constantPool;
|
||||||
private final ArrayList<ValueDescriptor> fields = new ArrayList<>();
|
private List<ValueDescriptor> fields = new ArrayList<>();
|
||||||
private Boolean simpleType; // calculated lazy
|
private Boolean simpleType; // calculated lazy
|
||||||
private boolean remove = true;
|
private boolean remove = true;
|
||||||
private long id;
|
private long id;
|
||||||
@ -183,6 +184,10 @@ public class Type implements Comparable<Type> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ValueDescriptor> getFields() {
|
public List<ValueDescriptor> getFields() {
|
||||||
|
if (fields instanceof ArrayList) {
|
||||||
|
((ArrayList<ValueDescriptor>) fields).trimToSize();
|
||||||
|
fields = Collections.unmodifiableList(fields);
|
||||||
|
}
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +221,7 @@ public class Type implements Comparable<Type> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void trimFields() {
|
void trimFields() {
|
||||||
fields.trimToSize();
|
getFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAnnotations(List<AnnotationElement> annotations) {
|
void setAnnotations(List<AnnotationElement> annotations) {
|
||||||
|
@ -102,6 +102,9 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String formatBytes(long bytes, String separation) {
|
public static String formatBytes(long bytes, String separation) {
|
||||||
|
if (bytes == 1) {
|
||||||
|
return "1 byte";
|
||||||
|
}
|
||||||
if (bytes < 1024) {
|
if (bytes < 1024) {
|
||||||
return bytes + " bytes";
|
return bytes + " bytes";
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ public final class ChunkHeader {
|
|||||||
return chunkSize;
|
return chunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDuration() {
|
public long getDurationNanos() {
|
||||||
return durationNanos;
|
return durationNanos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package jdk.jfr.internal.consumer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.jfr.internal.Type;
|
||||||
|
|
||||||
|
public abstract class RecordingInternals {
|
||||||
|
|
||||||
|
public static RecordingInternals INSTANCE;
|
||||||
|
|
||||||
|
public abstract boolean isLastEventInChunk(RecordingFile file);
|
||||||
|
|
||||||
|
public abstract Object getOffsetDataTime(RecordedObject event, String name);
|
||||||
|
|
||||||
|
public abstract List<Type> readTypes(RecordingFile file) throws IOException;
|
||||||
|
|
||||||
|
public abstract void sort(List<RecordedEvent> events);
|
||||||
|
|
||||||
|
}
|
127
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Assemble.java
Normal file
127
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Assemble.java
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class Assemble extends Command {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "assemble";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
return Collections.singletonList("<repository> <file>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Assemble leftover chunks from a disk repository into a recording file";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayOptionUsage(PrintStream stream) {
|
||||||
|
stream.println(" <repository> Directory where the repository is located");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" <file> Name of the recording file (.jfr) to create");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
ensureMinArgumentCount(options, 2);
|
||||||
|
ensureMaxArgumentCount(options, 2);
|
||||||
|
Path repository = getDirectory(options.pop());
|
||||||
|
|
||||||
|
Path file = Paths.get(options.pop());
|
||||||
|
ensureFileDoesNotExist(file);
|
||||||
|
ensureJFRFile(file);
|
||||||
|
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(file.toFile())) {
|
||||||
|
List<Path> files = listJFRFiles(repository);
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
throw new UserDataException("no *.jfr files found at " + repository);
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
println("Assembling files... ");
|
||||||
|
println();
|
||||||
|
transferTo(files, file, fos.getChannel());
|
||||||
|
println();
|
||||||
|
println("Finished.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UserDataException("could not open destination file " + file + ". " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Path> listJFRFiles(Path path) throws UserDataException {
|
||||||
|
try {
|
||||||
|
List<Path> files = new ArrayList<>();
|
||||||
|
if (Files.isDirectory(path)) {
|
||||||
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, "*.jfr")) {
|
||||||
|
for (Path p : stream) {
|
||||||
|
if (!Files.isDirectory(p) && Files.isReadable(p)) {
|
||||||
|
files.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files.sort((u, v) -> u.getFileName().compareTo(v.getFileName()));
|
||||||
|
return files;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new UserDataException("could not list *.jfr for directory " + path + ". " + ioe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transferTo(List<Path> sourceFiles, Path output, FileChannel out) throws UserDataException {
|
||||||
|
long pos = 0;
|
||||||
|
for (Path p : sourceFiles) {
|
||||||
|
println(" " + p.toString());
|
||||||
|
try (FileChannel sourceChannel = FileChannel.open(p)) {
|
||||||
|
long rem = Files.size(p);
|
||||||
|
while (rem > 0) {
|
||||||
|
long n = Math.min(rem, 1024 * 1024);
|
||||||
|
long w = out.transferFrom(sourceChannel, pos, n);
|
||||||
|
pos += w;
|
||||||
|
rem -= w;
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new UserDataException("could not copy recording chunk " + p + " to new file. " + ioe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
306
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Command.java
Normal file
306
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Command.java
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
abstract class Command {
|
||||||
|
public final static String title = "Tool for working with Flight Recorder files (.jfr)";
|
||||||
|
private final static Command HELP = new Help();
|
||||||
|
private final static List<Command> COMMANDS = createCommands();
|
||||||
|
|
||||||
|
private static List<Command> createCommands() {
|
||||||
|
List<Command> commands = new ArrayList<>();
|
||||||
|
commands.add(new Print());
|
||||||
|
commands.add(new Metadata());
|
||||||
|
commands.add(new Summary());
|
||||||
|
commands.add(new Assemble());
|
||||||
|
commands.add(new Disassemble());
|
||||||
|
commands.add(new Version());
|
||||||
|
commands.add(HELP);
|
||||||
|
return Collections.unmodifiableList(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void displayHelp() {
|
||||||
|
System.out.println(title);
|
||||||
|
System.out.println();
|
||||||
|
displayAvailableCommands(System.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public String getName();
|
||||||
|
|
||||||
|
abstract public String getDescription();
|
||||||
|
|
||||||
|
abstract public void execute(Deque<String> argList) throws UserSyntaxException, UserDataException;
|
||||||
|
|
||||||
|
protected String getTitle() {
|
||||||
|
return getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void displayAvailableCommands(PrintStream stream) {
|
||||||
|
boolean first = true;
|
||||||
|
for (Command c : Command.COMMANDS) {
|
||||||
|
if (!first) {
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
displayCommand(stream, c);
|
||||||
|
stream.println(" " + c.getDescription());
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void displayCommand(PrintStream stream, Command c) {
|
||||||
|
boolean firstSyntax = true;
|
||||||
|
String alias = buildAlias(c);
|
||||||
|
String initial = " jfr " + c.getName();
|
||||||
|
for (String syntaxLine : c.getOptionSyntax()) {
|
||||||
|
if (firstSyntax) {
|
||||||
|
if (syntaxLine.length() != 0) {
|
||||||
|
stream.println(initial + " " + syntaxLine + alias);
|
||||||
|
} else {
|
||||||
|
stream.println(initial + alias);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < initial.length(); i++) {
|
||||||
|
stream.print(" ");
|
||||||
|
}
|
||||||
|
stream.println(" " + syntaxLine);
|
||||||
|
}
|
||||||
|
firstSyntax = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildAlias(Command c) {
|
||||||
|
List<String> aliases = c.getAliases();
|
||||||
|
if (aliases.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (aliases.size() == 1) {
|
||||||
|
sb.append(" (alias ");
|
||||||
|
sb.append(aliases.get(0));
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
sb.append(" (aliases ");
|
||||||
|
for (int i = 0; i< aliases.size(); i ++ ) {
|
||||||
|
sb.append(aliases.get(i));
|
||||||
|
if (i < aliases.size() -1) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Command> getCommands() {
|
||||||
|
return COMMANDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Command valueOf(String commandName) {
|
||||||
|
for (Command command : COMMANDS) {
|
||||||
|
if (command.getName().equals(commandName)) {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
return Collections.singletonList("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayOptionUsage(PrintStream stream) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean acceptOption(Deque<String> options, String expected) throws UserSyntaxException {
|
||||||
|
if (expected.equals(options.peek())) {
|
||||||
|
if (options.size() < 2) {
|
||||||
|
throw new UserSyntaxException("missing value for " + options.peek());
|
||||||
|
}
|
||||||
|
options.remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void warnForWildcardExpansion(String option, String filter) throws UserDataException {
|
||||||
|
// Users should quote their wildcards to avoid expansion by the shell
|
||||||
|
try {
|
||||||
|
if (!filter.contains(File.pathSeparator)) {
|
||||||
|
Path p = Path.of(".", filter);
|
||||||
|
if (!Files.exists(p)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UserDataException("wildcards should be quoted, for example " + option + " \"Foo*\"");
|
||||||
|
} catch (InvalidPathException ipe) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean acceptFilterOption(Deque<String> options, String expected) throws UserSyntaxException {
|
||||||
|
if (!acceptOption(options, expected)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
throw new UserSyntaxException("missing filter after " + expected);
|
||||||
|
}
|
||||||
|
String filter = options.peek();
|
||||||
|
if (filter.startsWith("--")) {
|
||||||
|
throw new UserSyntaxException("missing filter after " + expected);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void ensureMaxArgumentCount(Deque<String> options, int maxCount) throws UserSyntaxException {
|
||||||
|
if (options.size() > maxCount) {
|
||||||
|
throw new UserSyntaxException("too many arguments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void ensureMinArgumentCount(Deque<String> options, int minCount) throws UserSyntaxException {
|
||||||
|
if (options.size() < minCount) {
|
||||||
|
throw new UserSyntaxException("too few arguments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected Path getDirectory(String pathText) throws UserDataException {
|
||||||
|
try {
|
||||||
|
Path path = Paths.get(pathText).toAbsolutePath();
|
||||||
|
if (!Files.exists((path))) {
|
||||||
|
throw new UserDataException("directory does not exist, " + pathText);
|
||||||
|
}
|
||||||
|
if (!Files.isDirectory(path)) {
|
||||||
|
throw new UserDataException("path must be directory, " + pathText);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
} catch (InvalidPathException ipe) {
|
||||||
|
throw new UserDataException("invalid path '" + pathText + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected Path getJFRInputFile(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
throw new UserSyntaxException("missing file");
|
||||||
|
}
|
||||||
|
String file = options.removeLast();
|
||||||
|
if (file.startsWith("--")) {
|
||||||
|
throw new UserSyntaxException("missing file");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Path path = Paths.get(file).toAbsolutePath();
|
||||||
|
ensureAccess(path);
|
||||||
|
ensureJFRFile(path);
|
||||||
|
return path;
|
||||||
|
} catch (IOError ioe) {
|
||||||
|
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
|
||||||
|
} catch (InvalidPathException ipe) {
|
||||||
|
throw new UserDataException("invalid path '" + file + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureAccess(Path path) throws UserDataException {
|
||||||
|
try (RandomAccessFile rad = new RandomAccessFile(path.toFile(), "r")) {
|
||||||
|
if (rad.length() == 0) {
|
||||||
|
throw new UserDataException("file is empty '" + path + "'");
|
||||||
|
}
|
||||||
|
rad.read(); // try to read 1 byte
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new UserDataException("could not find file '" + path + "'");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UserDataException("i/o error reading file '" + path + "', " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void couldNotReadError(Path p, IOException e) throws UserDataException {
|
||||||
|
throw new UserDataException("could not read recording at " + p.toAbsolutePath() + ". " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected Path ensureFileDoesNotExist(Path file) throws UserDataException {
|
||||||
|
if (Files.exists(file)) {
|
||||||
|
throw new UserDataException("file '" + file + "' already exists");
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void ensureJFRFile(Path path) throws UserDataException {
|
||||||
|
if (!path.toString().endsWith(".jfr")) {
|
||||||
|
throw new UserDataException("filename must end with '.jfr'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void displayUsage(PrintStream stream) {
|
||||||
|
displayCommand(stream, this);
|
||||||
|
stream.println();
|
||||||
|
displayOptionUsage(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void println() {
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void print(String text) {
|
||||||
|
System.out.print(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected void println(String text) {
|
||||||
|
System.out.println(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected boolean matches(String command) {
|
||||||
|
for (String s : getNames()) {
|
||||||
|
if (s.equals(command)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> getAliases() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getNames() {
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
names.add(getName());
|
||||||
|
names.addAll(getAliases());
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
}
|
250
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java
Normal file
250
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||||
|
import jdk.jfr.internal.consumer.RecordingInput;
|
||||||
|
|
||||||
|
final class Disassemble extends Command {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "disassemble";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add("[--output <directory>]");
|
||||||
|
list.add("[--max-chunks <chunks>]");
|
||||||
|
list.add("[--max-size <size>]");
|
||||||
|
list.add("<file>");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayOptionUsage(PrintStream stream) {
|
||||||
|
stream.println(" --output <directory> The location to write the disassembled file,");
|
||||||
|
stream.println(" by default the current directory");
|
||||||
|
stream.println("");
|
||||||
|
stream.println(" --max-chunks <chunks> Maximum number of chunks per disassembled file,");
|
||||||
|
stream.println(" by default 5. The chunk size varies, but is ");
|
||||||
|
stream.println(" typically around 15 MB.");
|
||||||
|
stream.println("");
|
||||||
|
stream.println(" --max-size <size> Maximum number of bytes per file.");
|
||||||
|
stream.println("");
|
||||||
|
stream.println(" <file> Location of the recording file (.jfr)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Disassamble a recording file into smaller files/chunks";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
throw new UserSyntaxException("missing file");
|
||||||
|
}
|
||||||
|
Path file = getJFRInputFile(options);
|
||||||
|
int maxChunks = Integer.MAX_VALUE;
|
||||||
|
int maxsize = Integer.MAX_VALUE;
|
||||||
|
String output = System.getProperty("user.dir");
|
||||||
|
int optionCount = options.size();
|
||||||
|
while (optionCount > 0) {
|
||||||
|
if (acceptOption(options, "--output")) {
|
||||||
|
output = options.pop();
|
||||||
|
}
|
||||||
|
if (acceptOption(options, "--max-size")) {
|
||||||
|
String value = options.pop();
|
||||||
|
try {
|
||||||
|
maxsize = Integer.parseInt(value);
|
||||||
|
if (maxsize < 1) {
|
||||||
|
throw new UserDataException("max size must be at least 1");
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
throw new UserDataException("not a valid value for --max-size.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (acceptOption(options, "--max-chunks")) {
|
||||||
|
String value = options.pop();
|
||||||
|
try {
|
||||||
|
maxChunks = Integer.parseInt(value);
|
||||||
|
if (maxChunks < 1) {
|
||||||
|
throw new UserDataException("max chunks must be at least 1.");
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
throw new UserDataException("not a valid value for --max-size.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (optionCount == options.size()) {
|
||||||
|
// No progress made
|
||||||
|
throw new UserSyntaxException("unknown option " + options.peek());
|
||||||
|
}
|
||||||
|
optionCount = options.size();
|
||||||
|
}
|
||||||
|
Path outputPath = getDirectory(output);
|
||||||
|
|
||||||
|
println();
|
||||||
|
println("Examining recording " + file + " ...");
|
||||||
|
List<Long> sizes;
|
||||||
|
if (maxsize != Integer.MAX_VALUE && maxChunks == Integer.MAX_VALUE) {
|
||||||
|
try {
|
||||||
|
long fileSize = Files.size(file);
|
||||||
|
if (maxsize >=fileSize) {
|
||||||
|
println();
|
||||||
|
println("File size (" + fileSize +") does not exceed max size (" + maxsize + ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UserDataException("unexpected i/o error when determining file size" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxsize == Integer.MAX_VALUE && maxChunks == Integer.MAX_VALUE) {
|
||||||
|
maxChunks = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sizes = findChunkSizes(file);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UserDataException("unexpected i/o error. " + e.getMessage());
|
||||||
|
}
|
||||||
|
if (maxsize == Integer.MAX_VALUE == sizes.size() <= maxChunks) {
|
||||||
|
throw new UserDataException("number of chunks in recording (" + sizes.size() + ") doesn't exceed max chunks (" + maxChunks + ")");
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
if (sizes.size() > 0) {
|
||||||
|
List<Long> combinedSizes = combineChunkSizes(sizes, maxChunks, maxsize);
|
||||||
|
print("File consists of " + sizes.size() + " chunks. The recording will be split into ");
|
||||||
|
println(combinedSizes.size() + " files");
|
||||||
|
println();
|
||||||
|
splitFile(outputPath, file, combinedSizes);
|
||||||
|
} else {
|
||||||
|
throw new UserDataException("no JFR chunks found in file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Long> findChunkSizes(Path p) throws IOException {
|
||||||
|
try (RecordingInput input = new RecordingInput(p.toFile())) {
|
||||||
|
List<Long> sizes = new ArrayList<>();
|
||||||
|
ChunkHeader ch = new ChunkHeader(input);
|
||||||
|
sizes.add(ch.getSize());
|
||||||
|
while (!ch.isLastChunk()) {
|
||||||
|
ch = ch.nextHeader();
|
||||||
|
sizes.add(ch.getSize());
|
||||||
|
}
|
||||||
|
return sizes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Long> combineChunkSizes(List<Long> sizes, int maxChunks, long maxSize) {
|
||||||
|
List<Long> reduced = new ArrayList<Long>();
|
||||||
|
int chunks = 1;
|
||||||
|
long fileSize = sizes.get(0);
|
||||||
|
for (int i = 1; i < sizes.size(); i++) {
|
||||||
|
long size = sizes.get(i);
|
||||||
|
if (fileSize + size > maxSize) {
|
||||||
|
reduced.add(fileSize);
|
||||||
|
chunks = 1;
|
||||||
|
fileSize = size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fileSize += size;
|
||||||
|
if (chunks == maxChunks) {
|
||||||
|
reduced.add(fileSize);
|
||||||
|
fileSize = 0;
|
||||||
|
chunks = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
chunks++;
|
||||||
|
}
|
||||||
|
if (fileSize != 0) {
|
||||||
|
reduced.add(fileSize);
|
||||||
|
}
|
||||||
|
return reduced;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void splitFile(Path directory, Path file, List<Long> splitPositions) throws UserDataException {
|
||||||
|
int padAmountZeros = String.valueOf(splitPositions.size() - 1).length();
|
||||||
|
String fileName = file.getFileName().toString();
|
||||||
|
String fileFormatter = fileName.subSequence(0, fileName.length() - 4) + "_%0" + padAmountZeros + "d.jfr";
|
||||||
|
for (int i = 0; i < splitPositions.size(); i++) {
|
||||||
|
String formattedFilename = String.format(fileFormatter, i);
|
||||||
|
try {
|
||||||
|
Path p = directory.resolve(formattedFilename);
|
||||||
|
if (Files.exists(p)) {
|
||||||
|
throw new UserDataException("can't create disassembled file " + p + ", a file with that name already exist");
|
||||||
|
}
|
||||||
|
} catch (InvalidPathException ipe) {
|
||||||
|
throw new UserDataException("can't construct path with filename" + formattedFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DataInputStream stream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.toFile())))) {
|
||||||
|
for (int i = 0; i < splitPositions.size(); i++) {
|
||||||
|
Long l = splitPositions.get(i);
|
||||||
|
byte[] bytes = readBytes(stream, l.intValue());
|
||||||
|
String formattedFilename = String.format(fileFormatter, i);
|
||||||
|
Path p = directory.resolve(formattedFilename);
|
||||||
|
File splittedFile = p.toFile();
|
||||||
|
println("Writing " + splittedFile + " ... " + bytes.length);
|
||||||
|
FileOutputStream fos = new FileOutputStream(splittedFile);
|
||||||
|
fos.write(bytes);
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new UserDataException("i/o error writing file " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readBytes(InputStream stream, int count) throws UserDataException, IOException {
|
||||||
|
byte[] data = new byte[count];
|
||||||
|
int totalRead = 0;
|
||||||
|
while (totalRead < data.length) {
|
||||||
|
int read = stream.read(data, totalRead, data.length - totalRead);
|
||||||
|
if (read == -1) {
|
||||||
|
throw new UserDataException("unexpected end of data");
|
||||||
|
}
|
||||||
|
totalRead += read;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
import jdk.jfr.Timespan;
|
||||||
|
import jdk.jfr.Timestamp;
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.jfr.internal.consumer.RecordingInternals;
|
||||||
|
|
||||||
|
abstract class EventPrintWriter extends StructuredWriter {
|
||||||
|
|
||||||
|
enum ValueType {
|
||||||
|
TIMESPAN, TIMESTAMP, OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static final String STACK_TRACE_FIELD = "stackTrace";
|
||||||
|
protected static final String EVENT_THREAD_FIELD = "eventThread";
|
||||||
|
|
||||||
|
private Predicate<EventType> eventFilter = x -> true;
|
||||||
|
private int stackDepth;
|
||||||
|
|
||||||
|
// cach that will speed up annotation lookup
|
||||||
|
private Map<ValueDescriptor, ValueType> typeOfValues = new HashMap<>();
|
||||||
|
|
||||||
|
EventPrintWriter(PrintWriter p) {
|
||||||
|
super(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected void print(List<RecordedEvent> events);
|
||||||
|
|
||||||
|
void print(Path source) throws FileNotFoundException, IOException {
|
||||||
|
List<RecordedEvent> events = new ArrayList<>(500_000);
|
||||||
|
printBegin();
|
||||||
|
try (RecordingFile file = new RecordingFile(source)) {
|
||||||
|
while (file.hasMoreEvents()) {
|
||||||
|
RecordedEvent event = file.readEvent();
|
||||||
|
if (acceptEvent(event)) {
|
||||||
|
events.add(event);
|
||||||
|
}
|
||||||
|
if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) {
|
||||||
|
RecordingInternals.INSTANCE.sort(events);
|
||||||
|
print(events);
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printEnd();
|
||||||
|
flush(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void printEnd() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void printBegin() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setEventFilter(Predicate<EventType> eventFilter) {
|
||||||
|
this.eventFilter = eventFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean acceptEvent(RecordedEvent event) {
|
||||||
|
return eventFilter.test(event.getEventType());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int getStackDepth() {
|
||||||
|
return stackDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isLateField(String name) {
|
||||||
|
return name.equals(EVENT_THREAD_FIELD) || name.equals(STACK_TRACE_FIELD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStackDepth(int stackDepth) {
|
||||||
|
this.stackDepth = stackDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getValue(RecordedObject object, ValueDescriptor v) {
|
||||||
|
ValueType valueType = typeOfValues.get(v);
|
||||||
|
if (valueType == null) {
|
||||||
|
valueType = determineValueType(v);
|
||||||
|
typeOfValues.put(v, valueType);
|
||||||
|
}
|
||||||
|
switch (valueType) {
|
||||||
|
case TIMESPAN:
|
||||||
|
return object.getDuration(v.getName());
|
||||||
|
case TIMESTAMP:
|
||||||
|
return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName());
|
||||||
|
default:
|
||||||
|
return object.getValue(v.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's expensive t check
|
||||||
|
private ValueType determineValueType(ValueDescriptor v) {
|
||||||
|
if (v.getAnnotation(Timespan.class) != null) {
|
||||||
|
return ValueType.TIMESPAN;
|
||||||
|
}
|
||||||
|
if (v.getAnnotation(Timestamp.class) != null) {
|
||||||
|
return ValueType.TIMESTAMP;
|
||||||
|
}
|
||||||
|
return ValueType.OTHER;
|
||||||
|
}
|
||||||
|
}
|
75
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Help.java
Normal file
75
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Help.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class Help extends Command {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "help";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
return Collections.singletonList("[<command>]");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> getAliases() {
|
||||||
|
return List.of("--help", "-h", "-?");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayOptionUsage(PrintStream stream) {
|
||||||
|
println(" <command> The name of the command to get help for");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Display all available commands, or help about a specific command";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
Command.displayHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ensureMaxArgumentCount(options, 1);
|
||||||
|
String commandName = options.remove();
|
||||||
|
Command c = Command.valueOf(commandName);
|
||||||
|
if (c == null) {
|
||||||
|
throw new UserDataException("unknown command '" + commandName + "'");
|
||||||
|
}
|
||||||
|
println(c.getTitle());
|
||||||
|
println();
|
||||||
|
c.displayUsage(System.out);
|
||||||
|
}
|
||||||
|
}
|
261
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java
Normal file
261
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedFrame;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
|
||||||
|
final class JSONWriter extends EventPrintWriter {
|
||||||
|
|
||||||
|
private boolean first = true;
|
||||||
|
|
||||||
|
public JSONWriter(PrintWriter writer) {
|
||||||
|
super(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void printBegin() {
|
||||||
|
printObjectBegin();
|
||||||
|
printDataStructureName("recording");
|
||||||
|
printObjectBegin();
|
||||||
|
printDataStructureName("events");
|
||||||
|
printArrayBegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void print(List<RecordedEvent> events) {
|
||||||
|
for (RecordedEvent event : events) {
|
||||||
|
printNewDataStructure(first, true, null);
|
||||||
|
printEvent(event);
|
||||||
|
flush(false);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void printEnd() {
|
||||||
|
printArrayEnd();;
|
||||||
|
printObjectEnd();
|
||||||
|
printObjectEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEvent(RecordedEvent event) {
|
||||||
|
printObjectBegin();
|
||||||
|
EventType type = event.getEventType();
|
||||||
|
printValue(true, false, "type", type.getName());
|
||||||
|
printNewDataStructure(false, false, "values");
|
||||||
|
printObjectBegin();
|
||||||
|
boolean first = true;
|
||||||
|
for (ValueDescriptor v : event.getFields()) {
|
||||||
|
printValueDescriptor(first, false, v, getValue(event, v));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
printObjectEnd();
|
||||||
|
printObjectEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printValue(boolean first, boolean arrayElement, String name, Object value) {
|
||||||
|
printNewDataStructure(first, arrayElement, name);
|
||||||
|
if (!printIfNull(value)) {
|
||||||
|
if (value instanceof Boolean) {
|
||||||
|
printAsString(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof Double) {
|
||||||
|
Double dValue = (Double) value;
|
||||||
|
if (Double.isNaN(dValue) || Double.isInfinite(dValue)) {
|
||||||
|
printNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printAsString(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof Float) {
|
||||||
|
Float fValue = (Float) value;
|
||||||
|
if (Float.isNaN(fValue) || Float.isInfinite(fValue)) {
|
||||||
|
printNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printAsString(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof Number) {
|
||||||
|
printAsString(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print("\"");
|
||||||
|
printEscaped(String.valueOf(value));
|
||||||
|
print("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printObject(RecordedObject object) {
|
||||||
|
printObjectBegin();
|
||||||
|
boolean first = true;
|
||||||
|
for (ValueDescriptor v : object.getFields()) {
|
||||||
|
printValueDescriptor(first, false, v, getValue(object, v));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
printObjectEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printArray(ValueDescriptor v, Object[] array) {
|
||||||
|
printArrayBegin();
|
||||||
|
boolean first = true;
|
||||||
|
int depth = 0;
|
||||||
|
for (Object arrayElement : array) {
|
||||||
|
if (!(arrayElement instanceof RecordedFrame) || depth < getStackDepth()) {
|
||||||
|
printValueDescriptor(first, true, v, arrayElement);
|
||||||
|
}
|
||||||
|
depth++;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
printArrayEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printValueDescriptor(boolean first, boolean arrayElement, ValueDescriptor vd, Object value) {
|
||||||
|
if (vd.isArray() && !arrayElement) {
|
||||||
|
printNewDataStructure(first, arrayElement, vd.getName());
|
||||||
|
if (!printIfNull(value)) {
|
||||||
|
printArray(vd, (Object[]) value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!vd.getFields().isEmpty()) {
|
||||||
|
printNewDataStructure(first, arrayElement, vd.getName());
|
||||||
|
if (!printIfNull(value)) {
|
||||||
|
printObject((RecordedObject) value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printValue(first, arrayElement, vd.getName(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printNewDataStructure(boolean first, boolean arrayElement, String name) {
|
||||||
|
if (!first) {
|
||||||
|
print(", ");
|
||||||
|
if (!arrayElement) {
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!arrayElement) {
|
||||||
|
printDataStructureName(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean printIfNull(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
printNull();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printNull() {
|
||||||
|
print("null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printDataStructureName(String text) {
|
||||||
|
printIndent();
|
||||||
|
print("\"");
|
||||||
|
print(text);
|
||||||
|
print("\": ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printObjectEnd() {
|
||||||
|
retract();
|
||||||
|
println();
|
||||||
|
printIndent();
|
||||||
|
print("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printObjectBegin() {
|
||||||
|
println("{");
|
||||||
|
indent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printArrayEnd() {
|
||||||
|
print("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printArrayBegin() {
|
||||||
|
print("[");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEscaped(String text) {
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
printEscaped(text.charAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEscaped(char c) {
|
||||||
|
if (c == '\b') {
|
||||||
|
print("\\b");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '\n') {
|
||||||
|
print("\\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '\t') {
|
||||||
|
print("\\t");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '\f') {
|
||||||
|
print("\\f");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '\r') {
|
||||||
|
print("\\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '\"') {
|
||||||
|
print("\\\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '\\') {
|
||||||
|
print("\\\\");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == '/') {
|
||||||
|
print("\\/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c > 0x7F || c < 32) {
|
||||||
|
print("\\u");
|
||||||
|
// 0x10000 will pad with zeros.
|
||||||
|
print(Integer.toHexString(0x10000 + (int) c).substring(1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print(c);
|
||||||
|
}
|
||||||
|
}
|
110
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Main.java
Normal file
110
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Main.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launcher class for the JDK_HOME\bin\jfr tool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class Main {
|
||||||
|
|
||||||
|
private static final int EXIT_OK = 0;
|
||||||
|
private static final int EXIT_FAILED = 1;
|
||||||
|
private static final int EXIT_WRONG_ARGUMENTS = 2;
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
Deque<String> argList = new LinkedList<>(Arrays.asList(args));
|
||||||
|
if (argList.isEmpty()) {
|
||||||
|
System.out.println(Command.title);
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Before using this tool, you must have a recording file.");
|
||||||
|
System.out.println("A file can be created by starting a recording from command line:");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" java -XX:StartFlightRecording:filename=recording.jfr,duration=30s ... ");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("A recording can also be started on already running Java Virtual Machine:");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jcmd (to list available pids)");
|
||||||
|
System.out.println(" jcmd <pid> JFR.start");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Recording data can be dumped to file using the JFR.dump command:");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jcmd <pid> JFR.dump filename=recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("The contents of the recording can then be printed, for example:");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr print recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr print --events CPULoad,GarbageCollection recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr print --json --events CPULoad recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr print --categories \"GC,JVM,Java*\" recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr print --events \"jdk.*\" --stack-depth 64 recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr summary recording.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(" jfr metadata recoding.jfr");
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("For more information about available commands, use 'jfr help'");
|
||||||
|
System.exit(EXIT_OK);
|
||||||
|
}
|
||||||
|
String command = argList.remove();
|
||||||
|
for (Command c : Command.getCommands()) {
|
||||||
|
if (c.matches(command)) {
|
||||||
|
try {
|
||||||
|
c.execute(argList);
|
||||||
|
System.exit(EXIT_OK);
|
||||||
|
} catch (UserDataException ude) {
|
||||||
|
System.err.println("jfr " + c.getName() + ": " + ude.getMessage());
|
||||||
|
System.exit(EXIT_FAILED);
|
||||||
|
} catch (UserSyntaxException use) {
|
||||||
|
System.err.println("jfr " + c.getName() + ": " + use.getMessage());
|
||||||
|
System.err.println();
|
||||||
|
System.err.println("Usage:");
|
||||||
|
System.err.println();
|
||||||
|
c.displayUsage(System.err);
|
||||||
|
System.exit(EXIT_WRONG_ARGUMENTS);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
System.err.println("jfr " + c.getName() + ": unexpected internal error, " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(EXIT_FAILED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.err.println("jfr: unknown command '" + command + "'");
|
||||||
|
System.err.println();
|
||||||
|
System.err.println("List of available commands:");
|
||||||
|
System.err.println();
|
||||||
|
Command.displayAvailableCommands(System.err);
|
||||||
|
System.exit(EXIT_WRONG_ARGUMENTS);
|
||||||
|
}
|
||||||
|
}
|
139
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java
Normal file
139
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.jfr.internal.Type;
|
||||||
|
import jdk.jfr.internal.consumer.RecordingInternals;
|
||||||
|
|
||||||
|
final class Metadata extends Command {
|
||||||
|
|
||||||
|
private static class TypeComparator implements Comparator<Type> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Type t1, Type t2) {
|
||||||
|
int g1 = groupValue(t1);
|
||||||
|
int g2 = groupValue(t2);
|
||||||
|
if (g1 == g2) {
|
||||||
|
String n1 = t1.getName();
|
||||||
|
String n2 = t2.getName();
|
||||||
|
String package1 = n1.substring(0, n1.lastIndexOf('.') + 1);
|
||||||
|
String package2 = n2.substring(0, n2.lastIndexOf('.') + 1);
|
||||||
|
|
||||||
|
if (package1.equals(package2)) {
|
||||||
|
return n1.compareTo(n2);
|
||||||
|
} else {
|
||||||
|
// Ensure that jdk.* are printed first
|
||||||
|
// This makes it easier to find user defined events at the end.
|
||||||
|
if (Type.SUPER_TYPE_EVENT.equals(t1.getSuperType()) && !package1.equals(package2)) {
|
||||||
|
if (package1.equals("jdk.jfr")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (package2.equals("jdk.jfr")) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return package1.compareTo(package2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Integer.compare(groupValue(t1), groupValue(t2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int groupValue(Type t) {
|
||||||
|
String superType = t.getSuperType();
|
||||||
|
if (superType == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (Type.SUPER_TYPE_SETTING.equals(superType)) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
if (Type.SUPER_TYPE_EVENT.equals(superType)) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
return 2; // reserved for enums in the future
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "metadata";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
return Collections.singletonList("<file>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Display event metadata, such as labels, descriptions and field layout";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
Path file = getJFRInputFile(options);
|
||||||
|
|
||||||
|
boolean showIds = false;
|
||||||
|
int optionCount = options.size();
|
||||||
|
while (optionCount > 0) {
|
||||||
|
if (acceptOption(options, "--ids")) {
|
||||||
|
showIds = true;
|
||||||
|
}
|
||||||
|
if (optionCount == options.size()) {
|
||||||
|
// No progress made
|
||||||
|
throw new UserSyntaxException("unknown option " + options.peek());
|
||||||
|
}
|
||||||
|
optionCount = options.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (PrintWriter pw = new PrintWriter(System.out)) {
|
||||||
|
PrettyWriter prettyWriter = new PrettyWriter(pw);
|
||||||
|
prettyWriter.setShowIds(showIds);
|
||||||
|
try (RecordingFile rf = new RecordingFile(file)) {
|
||||||
|
List<Type> types = RecordingInternals.INSTANCE.readTypes(rf);
|
||||||
|
Collections.sort(types, new TypeComparator());
|
||||||
|
for (Type type : types) {
|
||||||
|
prettyWriter.printType(type);
|
||||||
|
}
|
||||||
|
prettyWriter.flush(true);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
couldNotReadError(file, ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,501 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
import jdk.jfr.AnnotationElement;
|
||||||
|
import jdk.jfr.DataAmount;
|
||||||
|
import jdk.jfr.Frequency;
|
||||||
|
import jdk.jfr.MemoryAddress;
|
||||||
|
import jdk.jfr.Percentage;
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.consumer.RecordedClass;
|
||||||
|
import jdk.jfr.consumer.RecordedClassLoader;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedFrame;
|
||||||
|
import jdk.jfr.consumer.RecordedMethod;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
import jdk.jfr.consumer.RecordedStackTrace;
|
||||||
|
import jdk.jfr.consumer.RecordedThread;
|
||||||
|
import jdk.jfr.internal.PrivateAccess;
|
||||||
|
import jdk.jfr.internal.Type;
|
||||||
|
import jdk.jfr.internal.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print events in a human-readable format.
|
||||||
|
*
|
||||||
|
* This class is also used by {@link RecordedObject#toString()}
|
||||||
|
*/
|
||||||
|
public final class PrettyWriter extends EventPrintWriter {
|
||||||
|
private final static DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
|
||||||
|
private final static Long ZERO = 0L;
|
||||||
|
private boolean showIds;
|
||||||
|
|
||||||
|
public PrettyWriter(PrintWriter destination) {
|
||||||
|
super(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void print(List<RecordedEvent> events) {
|
||||||
|
for (RecordedEvent e : events) {
|
||||||
|
print(e);
|
||||||
|
flush(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printType(Type t) {
|
||||||
|
if (showIds) {
|
||||||
|
print("// id: ");
|
||||||
|
println(String.valueOf(t.getId()));
|
||||||
|
}
|
||||||
|
int commentIndex = t.getName().length() + 10;
|
||||||
|
String typeName = t.getName();
|
||||||
|
int index = typeName.lastIndexOf(".");
|
||||||
|
if (index != -1) {
|
||||||
|
println("@Name(\"" + typeName + "\")");
|
||||||
|
}
|
||||||
|
printAnnotations(commentIndex, t.getAnnotationElements());
|
||||||
|
print("class " + typeName.substring(index + 1));
|
||||||
|
String superType = t.getSuperType();
|
||||||
|
if (superType != null) {
|
||||||
|
print(" extends " + superType);
|
||||||
|
}
|
||||||
|
println(" {");
|
||||||
|
indent();
|
||||||
|
boolean first = true;
|
||||||
|
for (ValueDescriptor v : t.getFields()) {
|
||||||
|
printField(commentIndex, v, first);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
println("}");
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printField(int commentIndex, ValueDescriptor v, boolean first) {
|
||||||
|
if (!first) {
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
printAnnotations(commentIndex, v.getAnnotationElements());
|
||||||
|
printIndent();
|
||||||
|
Type vType = PrivateAccess.getInstance().getType(v);
|
||||||
|
if (Type.SUPER_TYPE_SETTING.equals(vType.getSuperType())) {
|
||||||
|
print("static ");
|
||||||
|
}
|
||||||
|
print(makeSimpleType(v.getTypeName()));
|
||||||
|
if (v.isArray()) {
|
||||||
|
print("[]");
|
||||||
|
}
|
||||||
|
print(" ");
|
||||||
|
print(v.getName());
|
||||||
|
print(";");
|
||||||
|
printCommentRef(commentIndex, v.getTypeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printCommentRef(int commentIndex, long typeId) {
|
||||||
|
if (showIds) {
|
||||||
|
int column = getColumn();
|
||||||
|
if (column > commentIndex) {
|
||||||
|
print(" ");
|
||||||
|
} else {
|
||||||
|
while (column < commentIndex) {
|
||||||
|
print(" ");
|
||||||
|
column++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println(" // id=" + typeId);
|
||||||
|
} else {
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printAnnotations(int commentIndex, List<AnnotationElement> annotations) {
|
||||||
|
for (AnnotationElement a : annotations) {
|
||||||
|
printIndent();
|
||||||
|
print("@");
|
||||||
|
print(makeSimpleType(a.getTypeName()));
|
||||||
|
List<ValueDescriptor> vs = a.getValueDescriptors();
|
||||||
|
if (!vs.isEmpty()) {
|
||||||
|
printAnnotation(a);
|
||||||
|
printCommentRef(commentIndex, a.getTypeId());
|
||||||
|
} else {
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printAnnotation(AnnotationElement a) {
|
||||||
|
StringJoiner sj = new StringJoiner(", ", "(", ")");
|
||||||
|
List<ValueDescriptor> vs = a.getValueDescriptors();
|
||||||
|
for (ValueDescriptor v : vs) {
|
||||||
|
Object o = a.getValue(v.getName());
|
||||||
|
if (vs.size() == 1 && v.getName().equals("value")) {
|
||||||
|
sj.add(textify(o));
|
||||||
|
} else {
|
||||||
|
sj.add(v.getName() + "=" + textify(o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print(sj.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String textify(Object o) {
|
||||||
|
if (o.getClass().isArray()) {
|
||||||
|
Object[] array = (Object[]) o;
|
||||||
|
if (array.length == 1) {
|
||||||
|
return quoteIfNeeded(array[0]);
|
||||||
|
}
|
||||||
|
StringJoiner s = new StringJoiner(", ", "{", "}");
|
||||||
|
for (Object ob : array) {
|
||||||
|
s.add(quoteIfNeeded(ob));
|
||||||
|
}
|
||||||
|
return s.toString();
|
||||||
|
} else {
|
||||||
|
return quoteIfNeeded(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String quoteIfNeeded(Object o) {
|
||||||
|
if (o instanceof String) {
|
||||||
|
return "\"" + o + "\"";
|
||||||
|
} else {
|
||||||
|
return String.valueOf(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeSimpleType(String typeName) {
|
||||||
|
int index = typeName.lastIndexOf(".");
|
||||||
|
return typeName.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print(RecordedEvent event) {
|
||||||
|
print(event.getEventType().getName(), " ");
|
||||||
|
println("{");
|
||||||
|
indent();
|
||||||
|
for (ValueDescriptor v : event.getFields()) {
|
||||||
|
String name = v.getName();
|
||||||
|
if (!isZeroDuration(event, name) && !isLateField(name)) {
|
||||||
|
printFieldValue(event, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.getThread() != null) {
|
||||||
|
printIndent();
|
||||||
|
print(EVENT_THREAD_FIELD + " = ");
|
||||||
|
printThread(event.getThread(), "");
|
||||||
|
}
|
||||||
|
if (event.getStackTrace() != null) {
|
||||||
|
printIndent();
|
||||||
|
print(STACK_TRACE_FIELD + " = ");
|
||||||
|
printStackTrace(event.getStackTrace());
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
printIndent();
|
||||||
|
println("}");
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isZeroDuration(RecordedEvent event, String name) {
|
||||||
|
return name.equals("duration") && ZERO.equals(event.getValue("duration"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printStackTrace(RecordedStackTrace stackTrace) {
|
||||||
|
println("[");
|
||||||
|
List<RecordedFrame> frames = stackTrace.getFrames();
|
||||||
|
indent();
|
||||||
|
int i = 0;
|
||||||
|
while (i < frames.size() && i < getStackDepth()) {
|
||||||
|
RecordedFrame frame = frames.get(i);
|
||||||
|
if (frame.isJavaFrame()) {
|
||||||
|
printIndent();
|
||||||
|
printValue(frame, null, "");
|
||||||
|
println();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stackTrace.isTruncated() || i == getStackDepth()) {
|
||||||
|
printIndent();
|
||||||
|
println("...");
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
printIndent();
|
||||||
|
println("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print(RecordedObject struct, String postFix) {
|
||||||
|
println("{");
|
||||||
|
indent();
|
||||||
|
for (ValueDescriptor v : struct.getFields()) {
|
||||||
|
printFieldValue(struct, v);
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
printIndent();
|
||||||
|
println("}" + postFix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printFieldValue(RecordedObject struct, ValueDescriptor v) {
|
||||||
|
printIndent();
|
||||||
|
print(v.getName(), " = ");
|
||||||
|
printValue(getValue(struct, v), v, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printArray(Object[] array) {
|
||||||
|
println("[");
|
||||||
|
indent();
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
printIndent();
|
||||||
|
printValue(array[i], null, i + 1 < array.length ? ", " : "");
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
printIndent();
|
||||||
|
println("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printValue(Object value, ValueDescriptor field, String postFix) {
|
||||||
|
if (value == null) {
|
||||||
|
println("null" + postFix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof RecordedObject) {
|
||||||
|
if (value instanceof RecordedThread) {
|
||||||
|
printThread((RecordedThread) value, postFix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof RecordedClass) {
|
||||||
|
printClass((RecordedClass) value, postFix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof RecordedClassLoader) {
|
||||||
|
printClassLoader((RecordedClassLoader) value, postFix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof RecordedFrame) {
|
||||||
|
RecordedFrame frame = (RecordedFrame) value;
|
||||||
|
if (frame.isJavaFrame()) {
|
||||||
|
printJavaFrame((RecordedFrame) value, postFix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value instanceof RecordedMethod) {
|
||||||
|
println(formatMethod((RecordedMethod) value));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print((RecordedObject) value, postFix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value.getClass().isArray()) {
|
||||||
|
printArray((Object[]) value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (field.getContentType() != null) {
|
||||||
|
if (printFormatted(field, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String text = String.valueOf(value);
|
||||||
|
if (value instanceof String) {
|
||||||
|
text = "\"" + text + "\"";
|
||||||
|
}
|
||||||
|
println(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printClassLoader(RecordedClassLoader cl, String postFix) {
|
||||||
|
// Purposely not printing class loader name to avoid cluttered output
|
||||||
|
RecordedClass clazz = cl.getType();
|
||||||
|
print(clazz == null ? "null" : clazz.getName());
|
||||||
|
if (clazz != null) {
|
||||||
|
print(" (");
|
||||||
|
print("id = ");
|
||||||
|
print(String.valueOf(cl.getId()));
|
||||||
|
println(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printJavaFrame(RecordedFrame f, String postFix) {
|
||||||
|
print(formatMethod(f.getMethod()));
|
||||||
|
int line = f.getLineNumber();
|
||||||
|
if (line >= 0) {
|
||||||
|
print(" line: " + line);
|
||||||
|
}
|
||||||
|
print(postFix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatMethod(RecordedMethod m) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(m.getType().getName());
|
||||||
|
sb.append(".");
|
||||||
|
sb.append(m.getName());
|
||||||
|
sb.append("(");
|
||||||
|
StringJoiner sj = new StringJoiner(", ");
|
||||||
|
String md = m.getDescriptor().replace("/", ".");
|
||||||
|
String parameter = md.substring(1, md.lastIndexOf(")"));
|
||||||
|
for (String qualifiedName : decodeDescriptors(parameter)) {
|
||||||
|
String typeName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
|
||||||
|
sj.add(typeName);
|
||||||
|
}
|
||||||
|
sb.append(sj);
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printClass(RecordedClass clazz, String postFix) {
|
||||||
|
RecordedClassLoader classLoader = clazz.getClassLoader();
|
||||||
|
String classLoaderName = "null";
|
||||||
|
if (classLoader != null) {
|
||||||
|
if (classLoader.getName() != null) {
|
||||||
|
classLoaderName = classLoader.getName();
|
||||||
|
} else {
|
||||||
|
classLoaderName = classLoader.getType().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String className = clazz.getName();
|
||||||
|
if (className.startsWith("[")) {
|
||||||
|
className = decodeDescriptors(className).get(0);
|
||||||
|
}
|
||||||
|
println(className + " (classLoader = " + classLoaderName + ")" + postFix);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> decodeDescriptors(String descriptor) {
|
||||||
|
List<String> descriptors = new ArrayList<>();
|
||||||
|
for (int index = 0; index < descriptor.length(); index++) {
|
||||||
|
String arrayBrackets = "";
|
||||||
|
while (descriptor.charAt(index) == '[') {
|
||||||
|
arrayBrackets += "[]";
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
char c = descriptor.charAt(index);
|
||||||
|
String type;
|
||||||
|
switch (c) {
|
||||||
|
case 'L':
|
||||||
|
int endIndex = descriptor.indexOf(';', index);
|
||||||
|
type = descriptor.substring(index + 1, endIndex);
|
||||||
|
index = endIndex;
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
type = "int";
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
type = "long";
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
type = "boolean";
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
type = "double";
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
type = "float";
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
type = "short";
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
type = "char";
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
type = "byte";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = "<unknown-descriptor-type>";
|
||||||
|
}
|
||||||
|
descriptors.add(type + arrayBrackets);
|
||||||
|
}
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printThread(RecordedThread thread, String postFix) {
|
||||||
|
long javaThreadId = thread.getJavaThreadId();
|
||||||
|
if (javaThreadId > 0) {
|
||||||
|
println("\"" + thread.getJavaName() + "\" (javaThreadId = " + thread.getJavaThreadId() + ")" + postFix);
|
||||||
|
} else {
|
||||||
|
println("\"" + thread.getOSName() + "\" (osThreadId = " + thread.getOSThreadId() + ")" + postFix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean printFormatted(ValueDescriptor field, Object value) {
|
||||||
|
if (value instanceof Duration) {
|
||||||
|
Duration d = (Duration) value;
|
||||||
|
double s = d.toNanosPart() / 1000_000_000.0 + d.toSecondsPart();
|
||||||
|
if (s < 1.0) {
|
||||||
|
if (s < 0.001) {
|
||||||
|
println(String.format("%.3f", s * 1_000_000) + " us");
|
||||||
|
} else {
|
||||||
|
println(String.format("%.3f", s * 1_000) + " ms");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (s < 1000.0) {
|
||||||
|
println(String.format("%.3f", s) + " s");
|
||||||
|
} else {
|
||||||
|
println(String.format("%.0f", s) + " s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value instanceof OffsetDateTime) {
|
||||||
|
OffsetDateTime zdt = (OffsetDateTime) value;
|
||||||
|
println(TIME_FORMAT.format(zdt));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Percentage percentage = field.getAnnotation(Percentage.class);
|
||||||
|
if (percentage != null) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
double d = ((Number) value).doubleValue();
|
||||||
|
println(String.format("%.2f", d * 100) + "%");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataAmount dataAmount = field.getAnnotation(DataAmount.class);
|
||||||
|
if (dataAmount != null) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
Number n = (Number) value;
|
||||||
|
String bytes = Utils.formatBytes(n.longValue(), " ");
|
||||||
|
if (field.getAnnotation(Frequency.class) != null) {
|
||||||
|
bytes += "/s";
|
||||||
|
}
|
||||||
|
println(bytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MemoryAddress memoryAddress = field.getAnnotation(MemoryAddress.class);
|
||||||
|
if (memoryAddress != null) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
long d = ((Number) value).longValue();
|
||||||
|
println(String.format("0x%08X", d));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowIds(boolean showIds) {
|
||||||
|
this.showIds = showIds;
|
||||||
|
}
|
||||||
|
}
|
262
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java
Normal file
262
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
|
||||||
|
final class Print extends Command {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "print";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add("[--xml|--json]");
|
||||||
|
list.add("[--categories <filter>]");
|
||||||
|
list.add("[--events <filter>]");
|
||||||
|
list.add("[--stack-depth <depth>]");
|
||||||
|
list.add("<file>");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTitle() {
|
||||||
|
return "Print contents of a recording file";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return getTitle() + ". See 'jfr help print' for details.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayOptionUsage(PrintStream stream) {
|
||||||
|
stream.println(" --xml Print recording in XML format");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" --json Print recording in JSON format");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" --categories <filter> Select events matching a category name.");
|
||||||
|
stream.println(" The filter is a comma-separated list of names,");
|
||||||
|
stream.println(" simple and/or qualified, and/or quoted glob patterns");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" --events <filter> Select events matching an event name.");
|
||||||
|
stream.println(" The filter is a comma-separated list of names,");
|
||||||
|
stream.println(" simple and/or qualified, and/or quoted glob patterns");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" --stack-depth <depth> Number of frames in stack traces, by default 5");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" <file> Location of the recording file (.jfr)");
|
||||||
|
stream.println();
|
||||||
|
stream.println();
|
||||||
|
stream.println("Example usage:");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" jfr print --events OldObjectSample recording.jfr");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" jfr print --events CPULoad,GarbageCollection recording.jfr");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" jfr print --categories \"GC,JVM,Java*\" recording.jfr");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" jfr print --events \"jdk.*\" --stack-depth 64 recording.jfr");
|
||||||
|
stream.println();
|
||||||
|
stream.println(" jfr print --json --events CPULoad recording.jfr");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
Path file = getJFRInputFile(options);
|
||||||
|
PrintWriter pw = new PrintWriter(System.out, false, Charset.forName("UTF-8"));
|
||||||
|
Predicate<EventType> eventFilter = null;
|
||||||
|
int stackDepth = 5;
|
||||||
|
EventPrintWriter eventWriter = null;
|
||||||
|
int optionCount = options.size();
|
||||||
|
while (optionCount > 0) {
|
||||||
|
if (acceptFilterOption(options, "--events")) {
|
||||||
|
String filter = options.remove();
|
||||||
|
warnForWildcardExpansion("--events", filter);
|
||||||
|
eventFilter = addEventFilter(filter, eventFilter);
|
||||||
|
}
|
||||||
|
if (acceptFilterOption(options, "--categories")) {
|
||||||
|
String filter = options.remove();
|
||||||
|
warnForWildcardExpansion("--categories", filter);
|
||||||
|
eventFilter = addCategoryFilter(filter, eventFilter);
|
||||||
|
}
|
||||||
|
if (acceptOption(options, "--stack-depth")) {
|
||||||
|
String value = options.pop();
|
||||||
|
try {
|
||||||
|
stackDepth = Integer.parseInt(value);
|
||||||
|
if (stackDepth < 0) {
|
||||||
|
throw new UserSyntaxException("stack depth must be zero or a positive integer.");
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
throw new UserSyntaxException("not a valid value for --stack-depth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (acceptFormatterOption(options, eventWriter, "--json")) {
|
||||||
|
eventWriter = new JSONWriter(pw);
|
||||||
|
}
|
||||||
|
if (acceptFormatterOption(options, eventWriter, "--xml")) {
|
||||||
|
eventWriter = new XMLWriter(pw);
|
||||||
|
}
|
||||||
|
if (optionCount == options.size()) {
|
||||||
|
// No progress made
|
||||||
|
throw new UserSyntaxException("unknown option " + options.peek());
|
||||||
|
}
|
||||||
|
optionCount = options.size();
|
||||||
|
}
|
||||||
|
if (eventWriter == null) {
|
||||||
|
eventWriter = new PrettyWriter(pw); // default to pretty printer
|
||||||
|
}
|
||||||
|
eventWriter.setStackDepth(stackDepth);
|
||||||
|
if (eventFilter != null) {
|
||||||
|
eventFilter = addCache(eventFilter, eventType -> eventType.getId());
|
||||||
|
eventWriter.setEventFilter(eventFilter);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
eventWriter.print(file);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
couldNotReadError(file, ioe);
|
||||||
|
}
|
||||||
|
pw.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean acceptFormatterOption(Deque<String> options, EventPrintWriter eventWriter, String expected) throws UserSyntaxException {
|
||||||
|
if (expected.equals(options.peek())) {
|
||||||
|
if (eventWriter != null) {
|
||||||
|
throw new UserSyntaxException("only one format can be specified at a time");
|
||||||
|
}
|
||||||
|
options.remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T, X> Predicate<T> addCache(final Predicate<T> filter, Function<T, X> cacheFunction) {
|
||||||
|
Map<X, Boolean> cache = new HashMap<>();
|
||||||
|
return t -> cache.computeIfAbsent(cacheFunction.apply(t), x -> filter.test(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> Predicate<T> recurseIfPossible(Predicate<T> filter) {
|
||||||
|
return x -> filter != null && filter.test(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Predicate<EventType> addCategoryFilter(String filterText, Predicate<EventType> eventFilter) throws UserSyntaxException {
|
||||||
|
List<String> filters = explodeFilter(filterText);
|
||||||
|
return recurseIfPossible(eventType -> {
|
||||||
|
for (String category : eventType.getCategoryNames()) {
|
||||||
|
for (String filter : filters) {
|
||||||
|
if (match(category, filter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (category.contains(" ") && acronomify(category).equals(filter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String acronomify(String multipleWords) {
|
||||||
|
boolean newWord = true;
|
||||||
|
String acronym = "";
|
||||||
|
for (char c : multipleWords.toCharArray()) {
|
||||||
|
if (newWord) {
|
||||||
|
if (Character.isAlphabetic(c) && Character.isUpperCase(c)) {
|
||||||
|
acronym += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newWord = Character.isWhitespace(c);
|
||||||
|
}
|
||||||
|
return acronym;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Predicate<EventType> addEventFilter(String filterText, final Predicate<EventType> eventFilter) throws UserSyntaxException {
|
||||||
|
List<String> filters = explodeFilter(filterText);
|
||||||
|
return recurseIfPossible(eventType -> {
|
||||||
|
for (String filter : filters) {
|
||||||
|
String fullEventName = eventType.getName();
|
||||||
|
if (match(fullEventName, filter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String eventName = fullEventName.substring(fullEventName.lastIndexOf(".") + 1);
|
||||||
|
if (match(eventName, filter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(String text, String filter) {
|
||||||
|
if (filter.length() == 0) {
|
||||||
|
// empty filter string matches if string is empty
|
||||||
|
return text.length() == 0;
|
||||||
|
}
|
||||||
|
if (filter.charAt(0) == '*') { // recursive check
|
||||||
|
filter = filter.substring(1);
|
||||||
|
for (int n = 0; n <= text.length(); n++) {
|
||||||
|
if (match(text.substring(n), filter))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (text.length() == 0) {
|
||||||
|
// empty string and non-empty filter does not match
|
||||||
|
return false;
|
||||||
|
} else if (filter.charAt(0) == '?') {
|
||||||
|
// eat any char and move on
|
||||||
|
return match(text.substring(1), filter.substring(1));
|
||||||
|
} else if (filter.charAt(0) == text.charAt(0)) {
|
||||||
|
// eat chars and move on
|
||||||
|
return match(text.substring(1), filter.substring(1));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> explodeFilter(String filter) throws UserSyntaxException {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (String s : filter.split(",")) {
|
||||||
|
s = s.trim();
|
||||||
|
if (!s.isEmpty()) {
|
||||||
|
list.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
abstract class StructuredWriter {
|
||||||
|
private final static String LINE_SEPARATOR = String.format("%n");
|
||||||
|
|
||||||
|
private final PrintWriter out;
|
||||||
|
private final StringBuilder builder = new StringBuilder(4000);
|
||||||
|
|
||||||
|
private char[] indentionArray = new char[0];
|
||||||
|
private int indent = 0;
|
||||||
|
private int column;
|
||||||
|
// print first event immediately so tool feels responsive
|
||||||
|
private boolean first = true;
|
||||||
|
|
||||||
|
StructuredWriter(PrintWriter p) {
|
||||||
|
out = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected int getColumn() {
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush to print writer
|
||||||
|
public final void flush(boolean hard) {
|
||||||
|
if (hard) {
|
||||||
|
out.print(builder.toString());
|
||||||
|
builder.setLength(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (first || builder.length() > 100_000) {
|
||||||
|
out.print(builder.toString());
|
||||||
|
builder.setLength(0);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void printIndent() {
|
||||||
|
builder.append(indentionArray, 0, indent);
|
||||||
|
column += indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void println() {
|
||||||
|
builder.append(LINE_SEPARATOR);
|
||||||
|
column = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void print(String... texts) {
|
||||||
|
for (String text : texts) {
|
||||||
|
print(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void printAsString(Object o) {
|
||||||
|
print(String.valueOf(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void print(String text) {
|
||||||
|
builder.append(text);
|
||||||
|
column += text.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void print(char c) {
|
||||||
|
builder.append(c);
|
||||||
|
column++;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void print(int value) {
|
||||||
|
print(String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void indent() {
|
||||||
|
indent += 2;
|
||||||
|
updateIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void retract() {
|
||||||
|
indent -= 2;
|
||||||
|
updateIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public void println(String text) {
|
||||||
|
print(text);
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIndent() {
|
||||||
|
if (indent > indentionArray.length) {
|
||||||
|
indentionArray = new char[indent];
|
||||||
|
for (int i = 0; i < indentionArray.length; i++) {
|
||||||
|
indentionArray[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Summary.java
Normal file
162
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Summary.java
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
import jdk.jfr.internal.MetadataDescriptor;
|
||||||
|
import jdk.jfr.internal.Type;
|
||||||
|
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||||
|
import jdk.jfr.internal.consumer.RecordingInput;
|
||||||
|
|
||||||
|
final class Summary extends Command {
|
||||||
|
private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withLocale(Locale.UK).withZone(ZoneOffset.UTC);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "summary";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Statistics {
|
||||||
|
Statistics(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
String name;
|
||||||
|
long count;
|
||||||
|
long size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOptionSyntax() {
|
||||||
|
return Collections.singletonList("<file>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayOptionUsage(PrintStream stream) {
|
||||||
|
stream.println(" <file> Location of the recording file (.jfr) to display information about");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Display general information about a recording file (.jfr)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||||
|
ensureMaxArgumentCount(options, 1);
|
||||||
|
Path p = getJFRInputFile(options);
|
||||||
|
try {
|
||||||
|
printInformation(p);
|
||||||
|
} catch (IOException e) {
|
||||||
|
couldNotReadError(p, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printInformation(Path p) throws IOException {
|
||||||
|
long totalDuration = 0;
|
||||||
|
long chunks = 0;
|
||||||
|
|
||||||
|
try (RecordingInput input = new RecordingInput(p.toFile())) {
|
||||||
|
ChunkHeader first = new ChunkHeader(input);
|
||||||
|
ChunkHeader ch = first;
|
||||||
|
String eventPrefix = Type.EVENT_NAME_PREFIX;
|
||||||
|
if (first.getMajor() == 1) {
|
||||||
|
eventPrefix = "com.oracle.jdk.";
|
||||||
|
}
|
||||||
|
HashMap<Long, Statistics> stats = new HashMap<>();
|
||||||
|
stats.put(0L, new Statistics(eventPrefix + "Metadata"));
|
||||||
|
stats.put(1L, new Statistics(eventPrefix + "CheckPoint"));
|
||||||
|
int minWidth = 0;
|
||||||
|
while (true) {
|
||||||
|
long chunkEnd = ch.getEnd();
|
||||||
|
MetadataDescriptor md = ch.readMetadata();
|
||||||
|
|
||||||
|
for (EventType eventType : md.getEventTypes()) {
|
||||||
|
stats.computeIfAbsent(eventType.getId(), (e) -> new Statistics(eventType.getName()));
|
||||||
|
minWidth = Math.max(minWidth, eventType.getName().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
totalDuration += ch.getDurationNanos();
|
||||||
|
chunks++;
|
||||||
|
input.position(ch.getEventStart());
|
||||||
|
while (input.position() < chunkEnd) {
|
||||||
|
long pos = input.position();
|
||||||
|
int size = input.readInt();
|
||||||
|
long eventTypeId = input.readLong();
|
||||||
|
Statistics s = stats.get(eventTypeId);
|
||||||
|
if (s != null) {
|
||||||
|
s.count++;
|
||||||
|
s.size += size;
|
||||||
|
}
|
||||||
|
input.position(pos + size);
|
||||||
|
}
|
||||||
|
if (ch.isLastChunk()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ch = ch.nextHeader();
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
long epochSeconds = first.getStartNanos() / 1_000_000_000L;
|
||||||
|
long adjustNanos = first.getStartNanos() - epochSeconds * 1_000_000_000L;
|
||||||
|
println(" Version: " + first.getMajor() + "." + first.getMinor());
|
||||||
|
println(" Chunks: " + chunks);
|
||||||
|
println(" Start: " + DATE_FORMAT.format(Instant.ofEpochSecond(epochSeconds, adjustNanos)) + " (UTC)");
|
||||||
|
println(" Duration: " + (totalDuration + 500_000_000) / 1_000_000_000 + " s");
|
||||||
|
|
||||||
|
List<Statistics> statsList = new ArrayList<>(stats.values());
|
||||||
|
Collections.sort(statsList, (u, v) -> Long.compare(v.count, u.count));
|
||||||
|
println();
|
||||||
|
String header = " Count Size (bytes) ";
|
||||||
|
String typeHeader = " Event Type";
|
||||||
|
minWidth = Math.max(minWidth, typeHeader.length());
|
||||||
|
println(typeHeader + pad(minWidth - typeHeader.length(), ' ') + header);
|
||||||
|
println(pad(minWidth + header.length(), '='));
|
||||||
|
for (Statistics s : statsList) {
|
||||||
|
System.out.printf(" %-" + minWidth + "s%10d %12d\n", s.name, s.count, s.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String pad(int count, char c) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception that is thrown if there is something wrong with the input, for instance
|
||||||
|
* a file that can't be read or a numerical value that is out of range.
|
||||||
|
* <p>
|
||||||
|
* When this exception is thrown, a user will typically not want to see the
|
||||||
|
* command line syntax, but instead information about what was wrong with the
|
||||||
|
* input.
|
||||||
|
*/
|
||||||
|
final class UserDataException extends Exception {
|
||||||
|
private static final long serialVersionUID = 6656457380115167810L;
|
||||||
|
/**
|
||||||
|
* The error message.
|
||||||
|
*
|
||||||
|
* The first letter should not be capitalized, so a context can be printed prior
|
||||||
|
* to the error message.
|
||||||
|
*
|
||||||
|
* @param errorMessage
|
||||||
|
*/
|
||||||
|
public UserDataException(String errorMessage) {
|
||||||
|
super(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception that is thrown if options don't follow the syntax of the command.
|
||||||
|
*/
|
||||||
|
final class UserSyntaxException extends Exception {
|
||||||
|
private static final long serialVersionUID = 3437009454344160933L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error message.
|
||||||
|
*
|
||||||
|
* The first letter should not be capitalized, so a context can be printed prior
|
||||||
|
* to the error message.
|
||||||
|
*
|
||||||
|
* @param errorMessage
|
||||||
|
*/
|
||||||
|
public UserSyntaxException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
50
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Version.java
Normal file
50
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Version.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class Version extends Command {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "version";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Display version of the jfr tool";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Deque<String> options) {
|
||||||
|
System.out.println("1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> getAliases() {
|
||||||
|
return List.of("--version");
|
||||||
|
}
|
||||||
|
}
|
200
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java
Normal file
200
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.internal.tool;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedFrame;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
|
||||||
|
final class XMLWriter extends EventPrintWriter {
|
||||||
|
public XMLWriter(PrintWriter destination) {
|
||||||
|
super(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void printBegin() {
|
||||||
|
println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
|
println("<recording xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
|
||||||
|
indent();
|
||||||
|
printIndent();
|
||||||
|
println("<events>");
|
||||||
|
indent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void printEnd() {
|
||||||
|
retract();
|
||||||
|
printIndent();
|
||||||
|
println("</events>");
|
||||||
|
retract();
|
||||||
|
println("</recording>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void print(List<RecordedEvent> events) {
|
||||||
|
for (RecordedEvent event : events) {
|
||||||
|
printEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEvent(RecordedEvent event) {
|
||||||
|
EventType type = event.getEventType();
|
||||||
|
printIndent();
|
||||||
|
print("<event");
|
||||||
|
printAttribute("type", type.getName());
|
||||||
|
print(">");
|
||||||
|
println();
|
||||||
|
indent();
|
||||||
|
for (ValueDescriptor v : event.getFields()) {
|
||||||
|
printValueDescriptor(v, getValue(event, v), -1);
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
printIndent();
|
||||||
|
println("</event>");
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printAttribute(String name, String value) {
|
||||||
|
print(" ", name, "=\"", value, "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printObject(RecordedObject struct) {
|
||||||
|
println();
|
||||||
|
indent();
|
||||||
|
for (ValueDescriptor v : struct.getFields()) {
|
||||||
|
printValueDescriptor(v, getValue(struct, v), -1);
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printArray(ValueDescriptor v, Object[] array) {
|
||||||
|
println();
|
||||||
|
indent();
|
||||||
|
int depth = 0;
|
||||||
|
for (int index = 0; index < array.length; index++) {
|
||||||
|
Object arrayElement = array[index];
|
||||||
|
if (!(arrayElement instanceof RecordedFrame) || depth < getStackDepth()) {
|
||||||
|
printValueDescriptor(v, array[index], index);
|
||||||
|
}
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
retract();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printValueDescriptor(ValueDescriptor vd, Object value, int index) {
|
||||||
|
boolean arrayElement = index != -1;
|
||||||
|
String name = arrayElement ? null : vd.getName();
|
||||||
|
if (vd.isArray() && !arrayElement) {
|
||||||
|
if (printBeginElement("array", name, value, index)) {
|
||||||
|
printArray(vd, (Object[]) value);
|
||||||
|
printIndent();
|
||||||
|
printEndElement("array");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!vd.getFields().isEmpty()) {
|
||||||
|
if (printBeginElement("struct", name, value, index)) {
|
||||||
|
printObject((RecordedObject) value);
|
||||||
|
printIndent();
|
||||||
|
printEndElement("struct");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (printBeginElement("value", name, value, index)) {
|
||||||
|
printEscaped(String.valueOf(value));
|
||||||
|
printEndElement("value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean printBeginElement(String elementName, String name, Object value, int index) {
|
||||||
|
printIndent();
|
||||||
|
print("<", elementName);
|
||||||
|
if (name != null) {
|
||||||
|
printAttribute("name", name);
|
||||||
|
}
|
||||||
|
if (index != -1) {
|
||||||
|
printAttribute("index", Integer.toString(index));
|
||||||
|
}
|
||||||
|
if (value == null) {
|
||||||
|
printAttribute("xsi:nil", "true");
|
||||||
|
println("/>");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value.getClass().isArray()) {
|
||||||
|
Object[] array = (Object[]) value;
|
||||||
|
printAttribute("size", Integer.toString(array.length));
|
||||||
|
}
|
||||||
|
print(">");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEndElement(String elementName) {
|
||||||
|
print("</");
|
||||||
|
print(elementName);
|
||||||
|
println(">");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEscaped(String text) {
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
printEscaped(text.charAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printEscaped(char c) {
|
||||||
|
if (c == 34) {
|
||||||
|
print(""");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == 38) {
|
||||||
|
print("&");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == 39) {
|
||||||
|
print("'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == 60) {
|
||||||
|
print("<");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == 62) {
|
||||||
|
print(">");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c > 0x7F) {
|
||||||
|
print("&#");
|
||||||
|
print((int) c);
|
||||||
|
print(';');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print(c);
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,12 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the API for JDK Flight Recorder.
|
* Defines the API for JDK Flight Recorder.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* <dl style="font-family:'DejaVu Sans', Arial, Helvetica, sans serif">
|
||||||
|
* <dt class="simpleTagLabel">Tool Guides:
|
||||||
|
* <dd>{@extLink jfr_tool_reference jfr}
|
||||||
|
* </dl>
|
||||||
*
|
*
|
||||||
* @moduleGraph
|
* @moduleGraph
|
||||||
* @since 9
|
* @since 9
|
||||||
|
125
test/jdk/jdk/jfr/tool/ExecuteHelper.java
Normal file
125
test/jdk/jdk/jfr/tool/ExecuteHelper.java
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import jdk.jfr.Configuration;
|
||||||
|
import jdk.jfr.Event;
|
||||||
|
import jdk.jfr.Recording;
|
||||||
|
import jdk.test.lib.Utils;;
|
||||||
|
|
||||||
|
final class ExecuteHelper {
|
||||||
|
|
||||||
|
public static Object[] array;
|
||||||
|
|
||||||
|
static class CustomEvent extends Event {
|
||||||
|
int intValue;
|
||||||
|
long longValue;
|
||||||
|
double doubliValue;
|
||||||
|
float floatValue;
|
||||||
|
String stringValue;
|
||||||
|
Short shortValue;
|
||||||
|
boolean booleanValue;
|
||||||
|
char charValue;
|
||||||
|
double trickyDouble;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void emitCustomEvents() {
|
||||||
|
// Custom events with potentially tricky values
|
||||||
|
CustomEvent event1 = new CustomEvent();
|
||||||
|
event1.trickyDouble = Double.NaN;
|
||||||
|
event1.intValue = Integer.MIN_VALUE;
|
||||||
|
event1.longValue = Long.MIN_VALUE;
|
||||||
|
event1.doubliValue = Double.MIN_VALUE;
|
||||||
|
event1.floatValue = Float.MIN_VALUE;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < 512; i++) {
|
||||||
|
sb.append((char) i);
|
||||||
|
}
|
||||||
|
sb.append("\u2324");
|
||||||
|
event1.stringValue = sb.toString();
|
||||||
|
event1.shortValue = Short.MIN_VALUE;
|
||||||
|
event1.booleanValue = true;
|
||||||
|
event1.booleanValue = false;
|
||||||
|
event1.charValue = '\b';
|
||||||
|
event1.commit();
|
||||||
|
|
||||||
|
CustomEvent event2 = new CustomEvent();
|
||||||
|
event2.trickyDouble = Double.NEGATIVE_INFINITY;
|
||||||
|
event2.intValue = Integer.MAX_VALUE;
|
||||||
|
event2.longValue = Long.MAX_VALUE;
|
||||||
|
event2.doubliValue = Double.MAX_VALUE;
|
||||||
|
event2.floatValue = Float.MAX_VALUE;
|
||||||
|
event2.stringValue = null;
|
||||||
|
event2.shortValue = Short.MAX_VALUE;
|
||||||
|
event2.booleanValue = false;
|
||||||
|
event2.charValue = 0;
|
||||||
|
event2.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Path createProfilingRecording() throws Exception {
|
||||||
|
Path file = Utils.createTempFile("profiling-recording", ".jfr");
|
||||||
|
// Create a recording with some data
|
||||||
|
try (Recording r = new Recording(Configuration.getConfiguration("profile"))) {
|
||||||
|
r.start();
|
||||||
|
|
||||||
|
// Allocation event
|
||||||
|
array = new Object[1000000];
|
||||||
|
array = null;
|
||||||
|
|
||||||
|
// Class loading event etc
|
||||||
|
provokeClassLoading();
|
||||||
|
|
||||||
|
// GC events
|
||||||
|
System.gc();
|
||||||
|
|
||||||
|
// ExecutionSample
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
|
while (System.currentTimeMillis() - t < 50) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other periodic events, i.e CPU load
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
r.stop();
|
||||||
|
r.dump(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void provokeClassLoading() {
|
||||||
|
// Matching a string with regexp
|
||||||
|
// is expected to load some classes and generate some VM events
|
||||||
|
Pattern p = Pattern.compile("a*b");
|
||||||
|
Matcher m = p.matcher("aaaaab");
|
||||||
|
m.matches();
|
||||||
|
}
|
||||||
|
}
|
146
test/jdk/jdk/jfr/tool/TestAssemble.java
Normal file
146
test/jdk/jdk/jfr/tool/TestAssemble.java
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import jdk.jfr.Event;
|
||||||
|
import jdk.jfr.Name;
|
||||||
|
import jdk.jfr.Recording;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.jfr.internal.Repository;
|
||||||
|
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||||
|
import jdk.test.lib.Asserts;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Test jfr reconstruct
|
||||||
|
* @key jfr
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @modules jdk.jfr/jdk.jfr.internal
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestAssemble
|
||||||
|
*/
|
||||||
|
public class TestAssemble {
|
||||||
|
|
||||||
|
@Name("Correlation")
|
||||||
|
static class CorrelationEvent extends Event {
|
||||||
|
int id;
|
||||||
|
}
|
||||||
|
private static int RECORDING_COUNT = 5;
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
// Create some disk recordings
|
||||||
|
Recording[] recordings = new Recording[5];
|
||||||
|
for (int i = 0; i < RECORDING_COUNT; i++) {
|
||||||
|
Recording r = new Recording();
|
||||||
|
r.setToDisk(true);
|
||||||
|
r.start();
|
||||||
|
CorrelationEvent ce = new CorrelationEvent();
|
||||||
|
ce.id = i;
|
||||||
|
ce.commit();
|
||||||
|
r.stop();
|
||||||
|
recordings[i] = r;
|
||||||
|
}
|
||||||
|
Path dir = Paths.get("reconstruction-parts");
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
|
||||||
|
long expectedCount = 0;
|
||||||
|
for (int i = 0; i < RECORDING_COUNT; i++) {
|
||||||
|
Path tmp = dir.resolve("chunk-part-" + i + ".jfr");
|
||||||
|
recordings[i].dump(tmp);
|
||||||
|
expectedCount += countEventInRecording(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
SafePath repository = Repository.getRepository().getRepositoryPath();
|
||||||
|
Path destinationPath = Paths.get("reconstructed.jfr");
|
||||||
|
|
||||||
|
String directory = repository.toString();
|
||||||
|
String destination = destinationPath.toAbsolutePath().toString();
|
||||||
|
|
||||||
|
// Test failure
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "assemble");
|
||||||
|
output.shouldContain("too few arguments");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "assemble", directory);
|
||||||
|
output.shouldContain("too few arguments");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "assemble", "not-a-directory", destination);
|
||||||
|
output.shouldContain("directory does not exist, not-a-directory");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "assemble", directory, "not-a-destination");
|
||||||
|
output.shouldContain("filename must end with '.jfr'");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","assemble", "--wrongOption", directory, destination);
|
||||||
|
output.shouldContain("too many arguments");
|
||||||
|
|
||||||
|
FileWriter fw = new FileWriter(destination);
|
||||||
|
fw.write('d');
|
||||||
|
fw.close();
|
||||||
|
output = ProcessTools.executeProcess("jfr", "assemble", directory, destination);
|
||||||
|
output.shouldContain("already exists");
|
||||||
|
Files.delete(destinationPath);
|
||||||
|
|
||||||
|
// test success
|
||||||
|
output = ProcessTools.executeProcess("jfr", "assemble", directory, destination);
|
||||||
|
System.out.println(output.getOutput());
|
||||||
|
output.shouldContain("Finished.");
|
||||||
|
|
||||||
|
long reconstructedCount = countEventInRecording(destinationPath);
|
||||||
|
Asserts.assertEquals(expectedCount, reconstructedCount);
|
||||||
|
// Cleanup
|
||||||
|
for (int i = 0; i < RECORDING_COUNT; i++) {
|
||||||
|
recordings[i].close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long countEventInRecording(Path file) throws IOException {
|
||||||
|
Integer lastId = -1;
|
||||||
|
try (RecordingFile rf = new RecordingFile(file)) {
|
||||||
|
long count = 0;
|
||||||
|
while (rf.hasMoreEvents()) {
|
||||||
|
RecordedEvent re = rf.readEvent();
|
||||||
|
if (re.getEventType().getName().equals("Correlation")) {
|
||||||
|
Integer id = re.getValue("id");
|
||||||
|
if (id < lastId) {
|
||||||
|
Asserts.fail("Expected chunk number to increase");
|
||||||
|
}
|
||||||
|
lastId = id;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
test/jdk/jdk/jfr/tool/TestDisassemble.java
Normal file
126
test/jdk/jdk/jfr/tool/TestDisassemble.java
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jdk.jfr.Configuration;
|
||||||
|
import jdk.jfr.Recording;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Test jfr split
|
||||||
|
* @key jfr
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestDisassemble
|
||||||
|
*/
|
||||||
|
public class TestDisassemble {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
|
||||||
|
String dateText = formatter.format(new Date());
|
||||||
|
|
||||||
|
Path recordingFileA = Paths.get("many-chunks-A-" + dateText + ".jfr");
|
||||||
|
Path recordingFileB = Paths.get("many-chunks-B-" + dateText + ".jfr");
|
||||||
|
Path recordingFileC = Paths.get("many-chunks-C-" + dateText + ".jfr");
|
||||||
|
makeRecordingWithChunks(6, recordingFileA);
|
||||||
|
Files.copy(recordingFileA, recordingFileB);
|
||||||
|
Files.copy(recordingFileA, recordingFileC);
|
||||||
|
|
||||||
|
String fileAText = recordingFileA.toAbsolutePath().toString();
|
||||||
|
String fileBText = recordingFileB.toAbsolutePath().toString();
|
||||||
|
String fileCText = recordingFileC.toAbsolutePath().toString();
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "disassemble");
|
||||||
|
output.shouldContain("missing file");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--wrongOption", fileAText);
|
||||||
|
output.shouldContain("unknown option");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--wrongOption", "1", fileAText);
|
||||||
|
output.shouldContain("unknown option");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--max-chunks", "-3", fileAText);
|
||||||
|
output.shouldContain("max chunks must be at least 1");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--max-chunks", "1000", fileAText);
|
||||||
|
output.shouldContain("number of chunks in recording");
|
||||||
|
output.shouldContain("doesn't exceed max chunks");
|
||||||
|
output = ProcessTools.executeProcess("jfr", "disassemble", fileAText); // maxchunks is 5 by
|
||||||
|
// default
|
||||||
|
System.out.println(output.getOutput());
|
||||||
|
System.out.println(fileAText);
|
||||||
|
verifyRecording(fileAText.substring(0, fileAText.length() - 4) + "_1.jfr");
|
||||||
|
verifyRecording(fileAText.substring(0, fileAText.length() - 4) + "_2.jfr");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--max-chunks", "2", fileBText);
|
||||||
|
|
||||||
|
verifyRecording(fileBText.substring(0, fileBText.length() - 4) + "_1.jfr");
|
||||||
|
verifyRecording(fileBText.substring(0, fileBText.length() - 4) + "_2.jfr");
|
||||||
|
verifyRecording(fileBText.substring(0, fileBText.length() - 4) + "_3.jfr");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--max-chunks", "2", fileBText);
|
||||||
|
output.shouldContain("file with that name already exist");
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
output = ProcessTools.executeProcess("jfr","disassemble", "--max-size", "500000", fileCText);
|
||||||
|
verifyRecording(fileCText.substring(0, fileCText.length() - 4) + "_1.jfr");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verifyRecording(String name) throws IOException {
|
||||||
|
System.out.println("Disassembling: " + name);
|
||||||
|
try (RecordingFile rf = new RecordingFile(Paths.get(name))) {
|
||||||
|
rf.readEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will create at least 2 * count + 1 chunks.
|
||||||
|
private static void makeRecordingWithChunks(int count, Path file) throws IOException, ParseException {
|
||||||
|
Recording main = new Recording(Configuration.getConfiguration("default"));
|
||||||
|
main.setToDisk(true);
|
||||||
|
main.start();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Recording r = new Recording();
|
||||||
|
r.setToDisk(true);
|
||||||
|
r.start();
|
||||||
|
r.stop();
|
||||||
|
r.close();
|
||||||
|
}
|
||||||
|
main.stop();
|
||||||
|
main.dump(file);
|
||||||
|
main.close();
|
||||||
|
}
|
||||||
|
}
|
57
test/jdk/jdk/jfr/tool/TestHelp.java
Normal file
57
test/jdk/jdk/jfr/tool/TestHelp.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Test help
|
||||||
|
* @key jfr
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestHelp
|
||||||
|
*/
|
||||||
|
public class TestHelp {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "help");
|
||||||
|
output.shouldContain("print");
|
||||||
|
output.shouldContain("assemble");
|
||||||
|
output.shouldContain("disassemble");
|
||||||
|
output.shouldContain("metadata");
|
||||||
|
output.shouldContain("summary");
|
||||||
|
output.shouldContain("help");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "help", "version");
|
||||||
|
output.shouldContain("Display version of the jfr tool");
|
||||||
|
output.shouldContain("jfr version");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "help", "wrongcommand");
|
||||||
|
output.shouldContain("unknown command 'wrongcommand'");
|
||||||
|
}
|
||||||
|
}
|
64
test/jdk/jdk/jfr/tool/TestMetadata.java
Normal file
64
test/jdk/jdk/jfr/tool/TestMetadata.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.tool;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Test jfr info
|
||||||
|
* @key jfr
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestMetadata
|
||||||
|
*/
|
||||||
|
public class TestMetadata {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
Path f = ExecuteHelper.createProfilingRecording().toAbsolutePath();
|
||||||
|
String file = f.toAbsolutePath().toString();
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "metadata");
|
||||||
|
output.shouldContain("missing file");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "metadata", "--wrongOption", file);
|
||||||
|
output.shouldContain("unknown option --wrongOption");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "metadata", file);
|
||||||
|
try (RecordingFile rf = new RecordingFile(f)) {
|
||||||
|
for (EventType t : rf.readEventTypes()) {
|
||||||
|
String name = t.getName();
|
||||||
|
name = name.substring(name.lastIndexOf(".") + 1);
|
||||||
|
output.shouldContain(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
test/jdk/jdk/jfr/tool/TestPrint.java
Normal file
62
test/jdk/jdk/jfr/tool/TestPrint.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import jdk.test.lib.Utils;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Test jfr print
|
||||||
|
* @key jfr
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestPrint
|
||||||
|
*/
|
||||||
|
public class TestPrint {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "print");
|
||||||
|
output.shouldContain("missing file");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "print", "missing.jfr");
|
||||||
|
output.shouldContain("could not find file ");
|
||||||
|
|
||||||
|
Path file = Utils.createTempFile("faked-print-file", ".jfr");
|
||||||
|
FileWriter fw = new FileWriter(file.toFile());
|
||||||
|
fw.write('d');
|
||||||
|
fw.close();
|
||||||
|
output = ProcessTools.executeProcess("jfr", "print", "--wrongOption", file.toAbsolutePath().toString());
|
||||||
|
output.shouldContain("unknown option");
|
||||||
|
Files.delete(file);
|
||||||
|
}
|
||||||
|
}
|
54
test/jdk/jdk/jfr/tool/TestPrintDefault.java
Normal file
54
test/jdk/jdk/jfr/tool/TestPrintDefault.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @key jfr
|
||||||
|
* @summary Tests print --json
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
*
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @modules java.scripting
|
||||||
|
* jdk.jfr
|
||||||
|
*
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestPrintDefault
|
||||||
|
*/
|
||||||
|
public class TestPrintDefault {
|
||||||
|
|
||||||
|
public static void main(String... args) throws Throwable {
|
||||||
|
|
||||||
|
Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath();
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "print", recordingFile.toString());
|
||||||
|
output.shouldContain("JVMInformation");
|
||||||
|
}
|
||||||
|
}
|
164
test/jdk/jdk/jfr/tool/TestPrintJSON.java
Normal file
164
test/jdk/jdk/jfr/tool/TestPrintJSON.java
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptEngineManager;
|
||||||
|
|
||||||
|
import jdk.jfr.Timespan;
|
||||||
|
import jdk.jfr.Timestamp;
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.nashorn.api.scripting.JSObject;
|
||||||
|
import jdk.test.lib.Asserts;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @key jfr
|
||||||
|
* @summary Tests print --json
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
*
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @modules jdk.scripting.nashorn
|
||||||
|
* jdk.jfr
|
||||||
|
*
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestPrintJSON
|
||||||
|
*/
|
||||||
|
public class TestPrintJSON {
|
||||||
|
|
||||||
|
public static void main(String... args) throws Throwable {
|
||||||
|
|
||||||
|
Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath();
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "print", "--json", "--stack-depth", "999", recordingFile.toString());
|
||||||
|
String json = output.getStdout();
|
||||||
|
|
||||||
|
// Parse JSON using Nashorn
|
||||||
|
String statement = "var jsonObject = " + json;
|
||||||
|
ScriptEngineManager factory = new ScriptEngineManager();
|
||||||
|
ScriptEngine engine = factory.getEngineByName("nashorn");
|
||||||
|
engine.eval(statement);
|
||||||
|
JSObject o = (JSObject) engine.get("jsonObject");
|
||||||
|
JSObject recording = (JSObject) o.getMember("recording");
|
||||||
|
JSObject jsonEvents = (JSObject) recording.getMember("events");
|
||||||
|
|
||||||
|
List<RecordedEvent> events = RecordingFile.readAllEvents(recordingFile);
|
||||||
|
Collections.sort(events, (e1, e2) -> e1.getEndTime().compareTo(e2.getEndTime()));
|
||||||
|
// Verify events are equal
|
||||||
|
Iterator<RecordedEvent> it = events.iterator();
|
||||||
|
|
||||||
|
for (Object jsonEvent : jsonEvents.values()) {
|
||||||
|
RecordedEvent recordedEvent = it.next();
|
||||||
|
String typeName = recordedEvent.getEventType().getName();
|
||||||
|
Asserts.assertEquals(typeName, ((JSObject) jsonEvent).getMember("type").toString());
|
||||||
|
assertEquals(jsonEvent, recordedEvent);
|
||||||
|
}
|
||||||
|
Asserts.assertFalse(events.size() != jsonEvents.values().size(), "Incorrect number of events");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEquals(Object jsonObject, Object jfrObject) throws Exception {
|
||||||
|
// Check object
|
||||||
|
if (jfrObject instanceof RecordedObject) {
|
||||||
|
JSObject values = (JSObject) ((JSObject) jsonObject).getMember("values");
|
||||||
|
RecordedObject recObject = (RecordedObject) jfrObject;
|
||||||
|
Asserts.assertEquals(values.values().size(), recObject.getFields().size());
|
||||||
|
for (ValueDescriptor v : recObject.getFields()) {
|
||||||
|
String name = v.getName();
|
||||||
|
Object jsonValue = values.getMember(name);
|
||||||
|
Object expectedValue = recObject.getValue(name);
|
||||||
|
if (v.getAnnotation(Timestamp.class) != null) {
|
||||||
|
// Make instant of OffsetDateTime
|
||||||
|
jsonValue = OffsetDateTime.parse("" + jsonValue).toInstant().toString();
|
||||||
|
expectedValue = recObject.getInstant(name);
|
||||||
|
}
|
||||||
|
if (v.getAnnotation(Timespan.class) != null) {
|
||||||
|
expectedValue = recObject.getDuration(name);
|
||||||
|
}
|
||||||
|
assertEquals(jsonValue, expectedValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check array
|
||||||
|
if (jfrObject != null && jfrObject.getClass().isArray()) {
|
||||||
|
Object[] jfrArray = (Object[]) jfrObject;
|
||||||
|
JSObject jsArray = (JSObject) jsonObject;
|
||||||
|
for (int i = 0; i < jfrArray.length; i++) {
|
||||||
|
assertEquals(jsArray.getSlot(i), jfrArray[i]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String jsonText = String.valueOf(jsonObject);
|
||||||
|
// Double.NaN / Double.Inifinity is not supported by JSON format,
|
||||||
|
// use null
|
||||||
|
if (jfrObject instanceof Double) {
|
||||||
|
double expected = ((Double) jfrObject);
|
||||||
|
if (Double.isInfinite(expected) || Double.isNaN(expected)) {
|
||||||
|
Asserts.assertEquals("null", jsonText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double value = Double.parseDouble(jsonText);
|
||||||
|
Asserts.assertEquals(expected, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Float.NaN / Float.Inifinity is not supported by JSON format,
|
||||||
|
// use null
|
||||||
|
if (jfrObject instanceof Float) {
|
||||||
|
float expected = ((Float) jfrObject);
|
||||||
|
if (Float.isInfinite(expected) || Float.isNaN(expected)) {
|
||||||
|
Asserts.assertEquals("null", jsonText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float value = Float.parseFloat(jsonText);
|
||||||
|
Asserts.assertEquals(expected, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jfrObject instanceof Integer) {
|
||||||
|
Integer expected = ((Integer) jfrObject);
|
||||||
|
double value = Double.parseDouble(jsonText);
|
||||||
|
Asserts.assertEquals(expected.doubleValue(), value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jfrObject instanceof Long) {
|
||||||
|
Long expected = ((Long) jfrObject);
|
||||||
|
double value = Double.parseDouble(jsonText);
|
||||||
|
Asserts.assertEquals(expected.doubleValue(), value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String jfrText = String.valueOf(jfrObject);
|
||||||
|
Asserts.assertEquals(jfrText, jsonText, "Primitive values don't match. JSON = " + jsonText);
|
||||||
|
}
|
||||||
|
}
|
261
test/jdk/jdk/jfr/tool/TestPrintXML.java
Normal file
261
test/jdk/jdk/jfr/tool/TestPrintXML.java
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jfr.tool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.parsers.SAXParser;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
import javax.xml.validation.Schema;
|
||||||
|
import javax.xml.validation.SchemaFactory;
|
||||||
|
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.SAXParseException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import jdk.jfr.Timespan;
|
||||||
|
import jdk.jfr.Timestamp;
|
||||||
|
import jdk.jfr.ValueDescriptor;
|
||||||
|
import jdk.jfr.consumer.RecordedEvent;
|
||||||
|
import jdk.jfr.consumer.RecordedObject;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @key jfr
|
||||||
|
* @summary Tests print --xml
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
*
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @modules java.scripting java.xml jdk.jfr
|
||||||
|
*
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestPrintXML
|
||||||
|
*/
|
||||||
|
public class TestPrintXML {
|
||||||
|
|
||||||
|
public static void main(String... args) throws Throwable {
|
||||||
|
|
||||||
|
Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath();
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "print", "--xml", "--stack-depth", "9999", recordingFile.toString());
|
||||||
|
System.out.println(recordingFile);
|
||||||
|
String xml = output.getStdout();
|
||||||
|
|
||||||
|
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||||
|
Schema schema = schemaFactory.newSchema(new File(System.getProperty("test.src"), "jfr.xsd"));
|
||||||
|
|
||||||
|
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||||
|
factory.setSchema(schema);
|
||||||
|
factory.setNamespaceAware(true);
|
||||||
|
|
||||||
|
SAXParser sp = factory.newSAXParser();
|
||||||
|
XMLReader xr = sp.getXMLReader();
|
||||||
|
RecordingHandler handler = new RecordingHandler();
|
||||||
|
xr.setContentHandler(handler);
|
||||||
|
xr.setErrorHandler(handler);
|
||||||
|
xr.parse(new InputSource(new StringReader(xml)));
|
||||||
|
|
||||||
|
// Verify that all data was written correctly
|
||||||
|
List<RecordedEvent> events = RecordingFile.readAllEvents(recordingFile);
|
||||||
|
Collections.sort(events, (e1, e2) -> e1.getEndTime().compareTo(e2.getEndTime()));
|
||||||
|
Iterator<RecordedEvent> it = events.iterator();
|
||||||
|
for (XMLEvent xmlEvent : handler.events) {
|
||||||
|
RecordedEvent re = it.next();
|
||||||
|
if (!compare(re, xmlEvent.values)) {
|
||||||
|
System.out.println("Expected:");
|
||||||
|
System.out.println("----------------------");
|
||||||
|
System.out.println(re);
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Was (XML)");
|
||||||
|
System.out.println("----------------------");
|
||||||
|
System.out.println(xmlEvent);
|
||||||
|
System.out.println();
|
||||||
|
throw new Exception("Event doesn't match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static boolean compare(Object eventObject, Object xmlObject) {
|
||||||
|
if (eventObject == null) {
|
||||||
|
return xmlObject == null;
|
||||||
|
}
|
||||||
|
if (eventObject instanceof RecordedObject) {
|
||||||
|
RecordedObject re = (RecordedObject) eventObject;
|
||||||
|
Map<String, Object> xmlMap = (Map<String, Object>) xmlObject;
|
||||||
|
List<ValueDescriptor> fields = re.getFields();
|
||||||
|
if (fields.size() != xmlMap.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (ValueDescriptor v : fields) {
|
||||||
|
String name = v.getName();
|
||||||
|
Object xmlValue = xmlMap.get(name);
|
||||||
|
Object expectedValue = re.getValue(name);
|
||||||
|
if (v.getAnnotation(Timestamp.class) != null) {
|
||||||
|
// Make instant of OffsetDateTime
|
||||||
|
xmlValue = OffsetDateTime.parse("" + xmlValue).toInstant().toString();
|
||||||
|
expectedValue = re.getInstant(name);
|
||||||
|
}
|
||||||
|
if (v.getAnnotation(Timespan.class) != null) {
|
||||||
|
expectedValue = re.getDuration(name);
|
||||||
|
}
|
||||||
|
if (!compare(expectedValue, xmlValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (eventObject.getClass().isArray()) {
|
||||||
|
Object[] array = (Object[]) eventObject;
|
||||||
|
Object[] xmlArray = (Object[]) xmlObject;
|
||||||
|
if (array.length != xmlArray.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
if (!compare(array[i], xmlArray[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String s1 = String.valueOf(eventObject);
|
||||||
|
String s2 = (String) xmlObject;
|
||||||
|
return s1.equals(s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class XMLEvent {
|
||||||
|
String name;
|
||||||
|
private Map<String, Object> values = new HashMap<>();
|
||||||
|
|
||||||
|
XMLEvent(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class RecordingHandler extends DefaultHandler {
|
||||||
|
|
||||||
|
private Stack<Object> objects = new Stack<>();
|
||||||
|
private Stack<SimpleEntry<String, String>> elements = new Stack<>();
|
||||||
|
private List<XMLEvent> events = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
|
||||||
|
elements.push(new SimpleEntry<>(attrs.getValue("name"), attrs.getValue("index")));
|
||||||
|
String nil = attrs.getValue("xsi:nil");
|
||||||
|
if ("true".equals(nil)) {
|
||||||
|
objects.push(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (qName) {
|
||||||
|
case "event":
|
||||||
|
objects.push(new XMLEvent(attrs.getValue("type")));
|
||||||
|
break;
|
||||||
|
case "struct":
|
||||||
|
objects.push(new HashMap<String, Object>());
|
||||||
|
break;
|
||||||
|
case "array":
|
||||||
|
objects.push(new Object[Integer.parseInt(attrs.getValue("size"))]);
|
||||||
|
break;
|
||||||
|
case "value":
|
||||||
|
objects.push(new StringBuilder());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||||
|
if (!objects.isEmpty()) {
|
||||||
|
Object o = objects.peek();
|
||||||
|
if (o instanceof StringBuilder) {
|
||||||
|
((StringBuilder) o).append(ch, start, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String qName) {
|
||||||
|
SimpleEntry<String, String> element = elements.pop();
|
||||||
|
switch (qName) {
|
||||||
|
case "event":
|
||||||
|
case "struct":
|
||||||
|
case "array":
|
||||||
|
case "value":
|
||||||
|
String name = element.getKey();
|
||||||
|
Object value = objects.pop();
|
||||||
|
if (objects.isEmpty()) {
|
||||||
|
events.add((XMLEvent) value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof StringBuilder) {
|
||||||
|
value = ((StringBuilder) value).toString();
|
||||||
|
}
|
||||||
|
Object parent = objects.peek();
|
||||||
|
if (parent instanceof XMLEvent) {
|
||||||
|
((XMLEvent) parent).values.put(name, value);
|
||||||
|
}
|
||||||
|
if (parent instanceof Map) {
|
||||||
|
((Map<String, Object>) parent).put(name, value);
|
||||||
|
}
|
||||||
|
if (parent != null && parent.getClass().isArray()) {
|
||||||
|
int index = Integer.parseInt(element.getValue());
|
||||||
|
((Object[]) parent)[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void warning(SAXParseException spe) throws SAXException {
|
||||||
|
throw new SAXException(spe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(SAXParseException spe) throws SAXException {
|
||||||
|
throw new SAXException(spe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fatalError(SAXParseException spe) throws SAXException {
|
||||||
|
throw new SAXException(spe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
test/jdk/jdk/jfr/tool/TestSummary.java
Normal file
63
test/jdk/jdk/jfr/tool/TestSummary.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.tool;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import jdk.jfr.EventType;
|
||||||
|
import jdk.jfr.consumer.RecordingFile;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Test jfr info
|
||||||
|
* @key jfr
|
||||||
|
* @requires vm.hasJFR
|
||||||
|
* @library /test/lib /test/jdk
|
||||||
|
* @run main/othervm jdk.jfr.tool.TestSummary
|
||||||
|
*/
|
||||||
|
public class TestSummary {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
Path f = ExecuteHelper.createProfilingRecording().toAbsolutePath();
|
||||||
|
String file = f.toAbsolutePath().toString();
|
||||||
|
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess("jfr", "summary");
|
||||||
|
output.shouldContain("missing file");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "summary", "--wrongOption", file);
|
||||||
|
output.shouldContain("too many arguments");
|
||||||
|
|
||||||
|
output = ProcessTools.executeProcess("jfr", "summary", file);
|
||||||
|
try (RecordingFile rf = new RecordingFile(f)) {
|
||||||
|
for (EventType t : rf.readEventTypes()) {
|
||||||
|
output.shouldContain(t.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.shouldContain("Version");
|
||||||
|
}
|
||||||
|
}
|
81
test/jdk/jdk/jfr/tool/jfr.xsd
Normal file
81
test/jdk/jdk/jfr/tool/jfr.xsd
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" elementFormDefault="unqualified" attributeFormDefault="unqualified" version="1.0">
|
||||||
|
<xs:element name="recording">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="1">
|
||||||
|
<xs:element name="events">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:element name="event" type="eventType" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="eventType">
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:choice>
|
||||||
|
<xs:element name="struct" nillable="true" type="structType" />
|
||||||
|
<xs:element name="array" nillable="true" type="arrayType" />
|
||||||
|
<xs:element name="value" nillable="true" type="valueType" />
|
||||||
|
</xs:choice>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute use="required" name="type" type="xs:string" />
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="structType">
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:choice>
|
||||||
|
<xs:element name="struct" nillable="true" type="structType" />
|
||||||
|
<xs:element name="array" nillable="true" type="arrayType" />
|
||||||
|
<xs:element name="value" nillable="true" type="valueType" />
|
||||||
|
</xs:choice>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute use="required" name="name" type="xs:string" />
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="arrayType">
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:choice>
|
||||||
|
<xs:element name="struct" nillable="true" type="structElement" />
|
||||||
|
<xs:element name="array" nillable="true" type="arrayElement" />
|
||||||
|
<xs:element name="value" nillable="true" type="valueElement" />
|
||||||
|
</xs:choice>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute use="required" name="size" type="xs:int" />
|
||||||
|
<xs:attribute use="required" name="name" type="xs:string" />
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="valueType">
|
||||||
|
<xs:simpleContent>
|
||||||
|
<xs:extension base="xs:string">
|
||||||
|
<xs:attribute use="required" name="name" type="xs:string" />
|
||||||
|
</xs:extension>
|
||||||
|
</xs:simpleContent>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="structElement">
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:choice>
|
||||||
|
<xs:element name="struct" nillable="true" type="structType" />
|
||||||
|
<xs:element name="array" nillable="true" type="arrayType" />
|
||||||
|
<xs:element name="value" nillable="true" type="valueType" />
|
||||||
|
</xs:choice>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute use="required" name="index" type="xs:int" />
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="arrayElement">
|
||||||
|
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:choice>
|
||||||
|
<xs:element name="value" nillable="true" type="valueType" />
|
||||||
|
<xs:element name="array" nillable="true" type="arrayType" />
|
||||||
|
<xs:element name="struct" nillable="true" type="structType" />
|
||||||
|
</xs:choice>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute use="required" name="index" type="xs:int" />
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="valueElement">
|
||||||
|
<xs:simpleContent>
|
||||||
|
<xs:extension base="xs:string">
|
||||||
|
<xs:attribute use="required" name="index" type="xs:int" />
|
||||||
|
</xs:extension>
|
||||||
|
</xs:simpleContent>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:schema>
|
@ -140,6 +140,7 @@ public class HelpFlagsTest extends TestHelper {
|
|||||||
new ToolHelpSpec("jdb", 1, 1, 1, 0, 1, 1, 0), // -?, -h, --help -help, Documents -help
|
new ToolHelpSpec("jdb", 1, 1, 1, 0, 1, 1, 0), // -?, -h, --help -help, Documents -help
|
||||||
new ToolHelpSpec("jdeprscan", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
|
new ToolHelpSpec("jdeprscan", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
|
||||||
new ToolHelpSpec("jdeps", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
|
new ToolHelpSpec("jdeps", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
|
||||||
|
new ToolHelpSpec("jfr", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
|
||||||
new ToolHelpSpec("jhsdb", 0, 0, 0, 0, 0, 0, 0), // none, prints help message anyways.
|
new ToolHelpSpec("jhsdb", 0, 0, 0, 0, 0, 0, 0), // none, prints help message anyways.
|
||||||
new ToolHelpSpec("jimage", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
|
new ToolHelpSpec("jimage", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
|
||||||
new ToolHelpSpec("jinfo", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
|
new ToolHelpSpec("jinfo", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
|
||||||
@ -224,6 +225,7 @@ public class HelpFlagsTest extends TestHelper {
|
|||||||
line.charAt(posAfter) != ',' &&
|
line.charAt(posAfter) != ',' &&
|
||||||
line.charAt(posAfter) != '[' && // jar
|
line.charAt(posAfter) != '[' && // jar
|
||||||
line.charAt(posAfter) != ']' && // jarsigner
|
line.charAt(posAfter) != ']' && // jarsigner
|
||||||
|
line.charAt(posAfter) != ')' && // jfr
|
||||||
line.charAt(posAfter) != '|' && // jstatd
|
line.charAt(posAfter) != '|' && // jstatd
|
||||||
line.charAt(posAfter) != ':' && // jps
|
line.charAt(posAfter) != ':' && // jps
|
||||||
line.charAt(posAfter) != '"') { // keytool
|
line.charAt(posAfter) != '"') { // keytool
|
||||||
|
@ -88,6 +88,7 @@ public class VersionCheck extends TestHelper {
|
|||||||
"jcontrol",
|
"jcontrol",
|
||||||
"jdeprscan",
|
"jdeprscan",
|
||||||
"jdeps",
|
"jdeps",
|
||||||
|
"jfr",
|
||||||
"jimage",
|
"jimage",
|
||||||
"jinfo",
|
"jinfo",
|
||||||
"jlink",
|
"jlink",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user