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

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

View File

@@ -0,0 +1,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());
}
}
}
}

View File

@@ -0,0 +1,306 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
abstract class Command {
public final static String title = "Tool for working with Flight Recorder files (.jfr)";
private final static Command HELP = new Help();
private final static List<Command> COMMANDS = createCommands();
private static List<Command> createCommands() {
List<Command> commands = new ArrayList<>();
commands.add(new Print());
commands.add(new Metadata());
commands.add(new Summary());
commands.add(new Assemble());
commands.add(new Disassemble());
commands.add(new Version());
commands.add(HELP);
return Collections.unmodifiableList(commands);
}
static void displayHelp() {
System.out.println(title);
System.out.println();
displayAvailableCommands(System.out);
}
abstract public String getName();
abstract public String getDescription();
abstract public void execute(Deque<String> argList) throws UserSyntaxException, UserDataException;
protected String getTitle() {
return getDescription();
}
static void displayAvailableCommands(PrintStream stream) {
boolean first = true;
for (Command c : Command.COMMANDS) {
if (!first) {
System.out.println();
}
displayCommand(stream, c);
stream.println(" " + c.getDescription());
first = false;
}
}
protected static void displayCommand(PrintStream stream, Command c) {
boolean firstSyntax = true;
String alias = buildAlias(c);
String initial = " jfr " + c.getName();
for (String syntaxLine : c.getOptionSyntax()) {
if (firstSyntax) {
if (syntaxLine.length() != 0) {
stream.println(initial + " " + syntaxLine + alias);
} else {
stream.println(initial + alias);
}
} else {
for (int i = 0; i < initial.length(); i++) {
stream.print(" ");
}
stream.println(" " + syntaxLine);
}
firstSyntax = false;
}
}
private static String buildAlias(Command c) {
List<String> aliases = c.getAliases();
if (aliases.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
if (aliases.size() == 1) {
sb.append(" (alias ");
sb.append(aliases.get(0));
sb.append(")");
return sb.toString();
}
sb.append(" (aliases ");
for (int i = 0; i< aliases.size(); i ++ ) {
sb.append(aliases.get(i));
if (i < aliases.size() -1) {
sb.append(", ");
}
}
sb.append(")");
return sb.toString();
}
public static List<Command> getCommands() {
return COMMANDS;
}
public static Command valueOf(String commandName) {
for (Command command : COMMANDS) {
if (command.getName().equals(commandName)) {
return command;
}
}
return null;
}
public List<String> getOptionSyntax() {
return Collections.singletonList("");
}
public void displayOptionUsage(PrintStream stream) {
}
protected boolean acceptOption(Deque<String> options, String expected) throws UserSyntaxException {
if (expected.equals(options.peek())) {
if (options.size() < 2) {
throw new UserSyntaxException("missing value for " + options.peek());
}
options.remove();
return true;
}
return false;
}
protected void warnForWildcardExpansion(String option, String filter) throws UserDataException {
// Users should quote their wildcards to avoid expansion by the shell
try {
if (!filter.contains(File.pathSeparator)) {
Path p = Paths.get(".", filter);
if (!Files.exists(p)) {
return;
}
}
throw new UserDataException("wildcards should be quoted, for example " + option + " \"Foo*\"");
} catch (InvalidPathException ipe) {
// ignore
}
}
protected boolean acceptFilterOption(Deque<String> options, String expected) throws UserSyntaxException {
if (!acceptOption(options, expected)) {
return false;
}
if (options.isEmpty()) {
throw new UserSyntaxException("missing filter after " + expected);
}
String filter = options.peek();
if (filter.startsWith("--")) {
throw new UserSyntaxException("missing filter after " + expected);
}
return true;
}
final protected void ensureMaxArgumentCount(Deque<String> options, int maxCount) throws UserSyntaxException {
if (options.size() > maxCount) {
throw new UserSyntaxException("too many arguments");
}
}
final protected void ensureMinArgumentCount(Deque<String> options, int minCount) throws UserSyntaxException {
if (options.size() < minCount) {
throw new UserSyntaxException("too few arguments");
}
}
final protected Path getDirectory(String pathText) throws UserDataException {
try {
Path path = Paths.get(pathText).toAbsolutePath();
if (!Files.exists((path))) {
throw new UserDataException("directory does not exist, " + pathText);
}
if (!Files.isDirectory(path)) {
throw new UserDataException("path must be directory, " + pathText);
}
return path;
} catch (InvalidPathException ipe) {
throw new UserDataException("invalid path '" + pathText + "'");
}
}
final protected Path getJFRInputFile(Deque<String> options) throws UserSyntaxException, UserDataException {
if (options.isEmpty()) {
throw new UserSyntaxException("missing file");
}
String file = options.removeLast();
if (file.startsWith("--")) {
throw new UserSyntaxException("missing file");
}
try {
Path path = Paths.get(file).toAbsolutePath();
ensureAccess(path);
ensureJFRFile(path);
return path;
} catch (IOError ioe) {
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
} catch (InvalidPathException ipe) {
throw new UserDataException("invalid path '" + file + "'");
}
}
private void ensureAccess(Path path) throws UserDataException {
try (RandomAccessFile rad = new RandomAccessFile(path.toFile(), "r")) {
if (rad.length() == 0) {
throw new UserDataException("file is empty '" + path + "'");
}
rad.read(); // try to read 1 byte
} catch (FileNotFoundException e) {
throw new UserDataException("could not open file " + e.getMessage());
} catch (IOException e) {
throw new UserDataException("i/o error reading file '" + path + "', " + e.getMessage());
}
}
final protected void couldNotReadError(Path p, IOException e) throws UserDataException {
throw new UserDataException("could not read recording at " + p.toAbsolutePath() + ". " + e.getMessage());
}
final protected Path ensureFileDoesNotExist(Path file) throws UserDataException {
if (Files.exists(file)) {
throw new UserDataException("file '" + file + "' already exists");
}
return file;
}
final protected void ensureJFRFile(Path path) throws UserDataException {
if (!path.toString().endsWith(".jfr")) {
throw new UserDataException("filename must end with '.jfr'");
}
}
protected void displayUsage(PrintStream stream) {
displayCommand(stream, this);
stream.println();
displayOptionUsage(stream);
}
final protected void println() {
System.out.println();
}
final protected void print(String text) {
System.out.print(text);
}
final protected void println(String text) {
System.out.println(text);
}
final protected boolean matches(String command) {
for (String s : getNames()) {
if (s.equals(command)) {
return true;
}
}
return false;
}
protected List<String> getAliases() {
return Collections.emptyList();
}
public List<String> getNames() {
List<String> names = new ArrayList<>();
names.add(getName());
names.addAll(getAliases());
return names;
}
}

View 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;
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import jdk.jfr.EventType;
import jdk.jfr.Timespan;
import jdk.jfr.Timestamp;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.consumer.RecordingInternals;
abstract class EventPrintWriter extends StructuredWriter {
enum ValueType {
TIMESPAN, TIMESTAMP, OTHER
}
protected static final String STACK_TRACE_FIELD = "stackTrace";
protected static final String EVENT_THREAD_FIELD = "eventThread";
private Predicate<EventType> eventFilter = x -> true;
private int stackDepth;
// cach that will speed up annotation lookup
private Map<ValueDescriptor, ValueType> typeOfValues = new HashMap<>();
EventPrintWriter(PrintWriter p) {
super(p);
}
abstract protected void print(List<RecordedEvent> events);
void print(Path source) throws FileNotFoundException, IOException {
List<RecordedEvent> events = new ArrayList<>(500_000);
printBegin();
try (RecordingFile file = new RecordingFile(source)) {
while (file.hasMoreEvents()) {
RecordedEvent event = file.readEvent();
if (acceptEvent(event)) {
events.add(event);
}
if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) {
RecordingInternals.INSTANCE.sort(events);
print(events);
events.clear();
}
}
}
printEnd();
flush(true);
}
protected void printEnd() {
}
protected void printBegin() {
}
public final void setEventFilter(Predicate<EventType> eventFilter) {
this.eventFilter = eventFilter;
}
protected final boolean acceptEvent(RecordedEvent event) {
return eventFilter.test(event.getEventType());
}
protected final int getStackDepth() {
return stackDepth;
}
protected final boolean isLateField(String name) {
return name.equals(EVENT_THREAD_FIELD) || name.equals(STACK_TRACE_FIELD);
}
public void setStackDepth(int stackDepth) {
this.stackDepth = stackDepth;
}
protected Object getValue(RecordedObject object, ValueDescriptor v) {
ValueType valueType = typeOfValues.get(v);
if (valueType == null) {
valueType = determineValueType(v);
typeOfValues.put(v, valueType);
}
switch (valueType) {
case TIMESPAN:
return object.getDuration(v.getName());
case TIMESTAMP:
return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName());
default:
return object.getValue(v.getName());
}
}
// It's expensive t check
private ValueType determineValueType(ValueDescriptor v) {
if (v.getAnnotation(Timespan.class) != null) {
return ValueType.TIMESPAN;
}
if (v.getAnnotation(Timestamp.class) != null) {
return ValueType.TIMESTAMP;
}
return ValueType.OTHER;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
final class Help extends Command {
@Override
public String getName() {
return "help";
}
@Override
public List<String> getOptionSyntax() {
return Collections.singletonList("[<command>]");
}
protected List<String> getAliases() {
return Arrays.asList("--help", "-h", "-?");
}
@Override
public void displayOptionUsage(PrintStream stream) {
println(" <command> The name of the command to get help for");
}
@Override
public String getDescription() {
return "Display all available commands, or help about a specific command";
}
@Override
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
if (options.isEmpty()) {
Command.displayHelp();
return;
}
ensureMaxArgumentCount(options, 1);
String commandName = options.remove();
Command c = Command.valueOf(commandName);
if (c == null) {
throw new UserDataException("unknown command '" + commandName + "'");
}
println(c.getTitle());
println();
c.displayUsage(System.out);
}
}

View File

@@ -0,0 +1,261 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.PrintWriter;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame;
import jdk.jfr.consumer.RecordedObject;
final class JSONWriter extends EventPrintWriter {
private boolean first = true;
public JSONWriter(PrintWriter writer) {
super(writer);
}
@Override
protected void printBegin() {
printObjectBegin();
printDataStructureName("recording");
printObjectBegin();
printDataStructureName("events");
printArrayBegin();
}
@Override
protected void print(List<RecordedEvent> events) {
for (RecordedEvent event : events) {
printNewDataStructure(first, true, null);
printEvent(event);
flush(false);
first = false;
}
}
@Override
protected void printEnd() {
printArrayEnd();;
printObjectEnd();
printObjectEnd();
}
private void printEvent(RecordedEvent event) {
printObjectBegin();
EventType type = event.getEventType();
printValue(true, false, "type", type.getName());
printNewDataStructure(false, false, "values");
printObjectBegin();
boolean first = true;
for (ValueDescriptor v : event.getFields()) {
printValueDescriptor(first, false, v, getValue(event, v));
first = false;
}
printObjectEnd();
printObjectEnd();
}
void printValue(boolean first, boolean arrayElement, String name, Object value) {
printNewDataStructure(first, arrayElement, name);
if (!printIfNull(value)) {
if (value instanceof Boolean) {
printAsString(value);
return;
}
if (value instanceof Double) {
Double dValue = (Double) value;
if (Double.isNaN(dValue) || Double.isInfinite(dValue)) {
printNull();
return;
}
printAsString(value);
return;
}
if (value instanceof Float) {
Float fValue = (Float) value;
if (Float.isNaN(fValue) || Float.isInfinite(fValue)) {
printNull();
return;
}
printAsString(value);
return;
}
if (value instanceof Number) {
printAsString(value);
return;
}
print("\"");
printEscaped(String.valueOf(value));
print("\"");
}
}
public void printObject(RecordedObject object) {
printObjectBegin();
boolean first = true;
for (ValueDescriptor v : object.getFields()) {
printValueDescriptor(first, false, v, getValue(object, v));
first = false;
}
printObjectEnd();
}
private void printArray(ValueDescriptor v, Object[] array) {
printArrayBegin();
boolean first = true;
int depth = 0;
for (Object arrayElement : array) {
if (!(arrayElement instanceof RecordedFrame) || depth < getStackDepth()) {
printValueDescriptor(first, true, v, arrayElement);
}
depth++;
first = false;
}
printArrayEnd();
}
private void printValueDescriptor(boolean first, boolean arrayElement, ValueDescriptor vd, Object value) {
if (vd.isArray() && !arrayElement) {
printNewDataStructure(first, arrayElement, vd.getName());
if (!printIfNull(value)) {
printArray(vd, (Object[]) value);
}
return;
}
if (!vd.getFields().isEmpty()) {
printNewDataStructure(first, arrayElement, vd.getName());
if (!printIfNull(value)) {
printObject((RecordedObject) value);
}
return;
}
printValue(first, arrayElement, vd.getName(), value);
}
private void printNewDataStructure(boolean first, boolean arrayElement, String name) {
if (!first) {
print(", ");
if (!arrayElement) {
println();
}
}
if (!arrayElement) {
printDataStructureName(name);
}
}
private boolean printIfNull(Object value) {
if (value == null) {
printNull();
return true;
}
return false;
}
private void printNull() {
print("null");
}
private void printDataStructureName(String text) {
printIndent();
print("\"");
printEscaped(text);
print("\": ");
}
private void printObjectEnd() {
retract();
println();
printIndent();
print("}");
}
private void printObjectBegin() {
println("{");
indent();
}
private void printArrayEnd() {
print("]");
}
private void printArrayBegin() {
print("[");
}
private void printEscaped(String text) {
for (int i = 0; i < text.length(); i++) {
printEscaped(text.charAt(i));
}
}
private void printEscaped(char c) {
if (c == '\b') {
print("\\b");
return;
}
if (c == '\n') {
print("\\n");
return;
}
if (c == '\t') {
print("\\t");
return;
}
if (c == '\f') {
print("\\f");
return;
}
if (c == '\r') {
print("\\r");
return;
}
if (c == '\"') {
print("\\\"");
return;
}
if (c == '\\') {
print("\\\\");
return;
}
if (c == '/') {
print("\\/");
return;
}
if (c > 0x7F || c < 32) {
print("\\u");
// 0x10000 will pad with zeros.
print(Integer.toHexString(0x10000 + (int) c).substring(1));
return;
}
print(c);
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
/**
* Launcher class for the JDK_HOME\bin\jfr tool
*
*/
public final class Main {
private static final int EXIT_OK = 0;
private static final int EXIT_FAILED = 1;
private static final int EXIT_WRONG_ARGUMENTS = 2;
public static void main(String... args) {
Deque<String> argList = new LinkedList<>(Arrays.asList(args));
if (argList.isEmpty()) {
System.out.println(Command.title);
System.out.println();
System.out.println("Before using this tool, you must have a recording file.");
System.out.println("A file can be created by starting a recording from command line:");
System.out.println();
System.out.println(" java -XX:StartFlightRecording:filename=recording.jfr,duration=30s ... ");
System.out.println();
System.out.println("A recording can also be started on already running Java Virtual Machine:");
System.out.println();
System.out.println(" jcmd (to list available pids)");
System.out.println(" jcmd <pid> JFR.start");
System.out.println();
System.out.println("Recording data can be dumped to file using the JFR.dump command:");
System.out.println();
System.out.println(" jcmd <pid> JFR.dump filename=recording.jfr");
System.out.println();
System.out.println("The contents of the recording can then be printed, for example:");
System.out.println();
System.out.println(" jfr print recording.jfr");
System.out.println();
System.out.println(" jfr print --events CPULoad,GarbageCollection recording.jfr");
System.out.println();
System.out.println(" jfr print --json --events CPULoad recording.jfr");
System.out.println();
System.out.println(" jfr print --categories \"GC,JVM,Java*\" recording.jfr");
System.out.println();
System.out.println(" jfr print --events \"jdk.*\" --stack-depth 64 recording.jfr");
System.out.println();
System.out.println(" jfr summary recording.jfr");
System.out.println();
System.out.println(" jfr metadata recording.jfr");
System.out.println();
System.out.println("For more information about available commands, use 'jfr help'");
System.exit(EXIT_OK);
}
String command = argList.remove();
for (Command c : Command.getCommands()) {
if (c.matches(command)) {
try {
c.execute(argList);
System.exit(EXIT_OK);
} catch (UserDataException ude) {
System.err.println("jfr " + c.getName() + ": " + ude.getMessage());
System.exit(EXIT_FAILED);
} catch (UserSyntaxException use) {
System.err.println("jfr " + c.getName() + ": " + use.getMessage());
System.err.println();
System.err.println("Usage:");
System.err.println();
c.displayUsage(System.err);
System.exit(EXIT_WRONG_ARGUMENTS);
} catch (Throwable e) {
System.err.println("jfr " + c.getName() + ": unexpected internal error, " + e.getMessage());
e.printStackTrace();
System.exit(EXIT_FAILED);
}
}
}
System.err.println("jfr: unknown command '" + command + "'");
System.err.println();
System.err.println("List of available commands:");
System.err.println();
Command.displayAvailableCommands(System.err);
System.exit(EXIT_WRONG_ARGUMENTS);
}
}

View 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);
}
}
}
}

View File

@@ -0,0 +1,634 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.PrintWriter;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import jdk.jfr.AnnotationElement;
import jdk.jfr.DataAmount;
import jdk.jfr.Frequency;
import jdk.jfr.MemoryAddress;
import jdk.jfr.Percentage;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedClassLoader;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame;
import jdk.jfr.consumer.RecordedMethod;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.consumer.RecordedThread;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.Utils;
/**
* Print events in a human-readable format.
*
* This class is also used by {@link RecordedObject#toString()}
*/
public final class PrettyWriter extends EventPrintWriter {
private static final String TYPE_OLD_OBJECT = Type.TYPES_PREFIX + "OldObject";
private final static DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
private final static Long ZERO = 0L;
private boolean showIds;
private RecordedEvent currentEvent;
public PrettyWriter(PrintWriter destination) {
super(destination);
}
@Override
protected void print(List<RecordedEvent> events) {
for (RecordedEvent e : events) {
print(e);
flush(false);
}
}
public void printType(Type t) {
if (showIds) {
print("// id: ");
println(String.valueOf(t.getId()));
}
int commentIndex = t.getName().length() + 10;
String typeName = t.getName();
int index = typeName.lastIndexOf(".");
if (index != -1) {
println("@Name(\"" + typeName + "\")");
}
printAnnotations(commentIndex, t.getAnnotationElements());
print("class " + typeName.substring(index + 1));
String superType = t.getSuperType();
if (superType != null) {
print(" extends " + superType);
}
println(" {");
indent();
boolean first = true;
for (ValueDescriptor v : t.getFields()) {
printField(commentIndex, v, first);
first = false;
}
retract();
println("}");
println();
}
private void printField(int commentIndex, ValueDescriptor v, boolean first) {
if (!first) {
println();
}
printAnnotations(commentIndex, v.getAnnotationElements());
printIndent();
Type vType = PrivateAccess.getInstance().getType(v);
if (Type.SUPER_TYPE_SETTING.equals(vType.getSuperType())) {
print("static ");
}
print(makeSimpleType(v.getTypeName()));
if (v.isArray()) {
print("[]");
}
print(" ");
print(v.getName());
print(";");
printCommentRef(commentIndex, v.getTypeId());
}
private void printCommentRef(int commentIndex, long typeId) {
if (showIds) {
int column = getColumn();
if (column > commentIndex) {
print(" ");
} else {
while (column < commentIndex) {
print(" ");
column++;
}
}
println(" // id=" + typeId);
} else {
println();
}
}
private void printAnnotations(int commentIndex, List<AnnotationElement> annotations) {
for (AnnotationElement a : annotations) {
printIndent();
print("@");
print(makeSimpleType(a.getTypeName()));
List<ValueDescriptor> vs = a.getValueDescriptors();
if (!vs.isEmpty()) {
printAnnotation(a);
printCommentRef(commentIndex, a.getTypeId());
} else {
println();
}
}
}
private void printAnnotation(AnnotationElement a) {
StringJoiner sj = new StringJoiner(", ", "(", ")");
List<ValueDescriptor> vs = a.getValueDescriptors();
for (ValueDescriptor v : vs) {
Object o = a.getValue(v.getName());
if (vs.size() == 1 && v.getName().equals("value")) {
sj.add(textify(o));
} else {
sj.add(v.getName() + "=" + textify(o));
}
}
print(sj.toString());
}
private String textify(Object o) {
if (o.getClass().isArray()) {
Object[] array = (Object[]) o;
if (array.length == 1) {
return quoteIfNeeded(array[0]);
}
StringJoiner s = new StringJoiner(", ", "{", "}");
for (Object ob : array) {
s.add(quoteIfNeeded(ob));
}
return s.toString();
} else {
return quoteIfNeeded(o);
}
}
private String quoteIfNeeded(Object o) {
if (o instanceof String) {
return "\"" + o + "\"";
} else {
return String.valueOf(o);
}
}
private String makeSimpleType(String typeName) {
int index = typeName.lastIndexOf(".");
return typeName.substring(index + 1);
}
public void print(RecordedEvent event) {
currentEvent = event;
print(event.getEventType().getName(), " ");
println("{");
indent();
for (ValueDescriptor v : event.getFields()) {
String name = v.getName();
if (!isZeroDuration(event, name) && !isLateField(name)) {
printFieldValue(event, v);
}
}
if (event.getThread() != null) {
printIndent();
print(EVENT_THREAD_FIELD + " = ");
printThread(event.getThread(), "");
}
if (event.getStackTrace() != null) {
printIndent();
print(STACK_TRACE_FIELD + " = ");
printStackTrace(event.getStackTrace());
}
retract();
printIndent();
println("}");
println();
}
private boolean isZeroDuration(RecordedEvent event, String name) {
return name.equals("duration") && ZERO.equals(event.getValue("duration"));
}
private void printStackTrace(RecordedStackTrace stackTrace) {
println("[");
List<RecordedFrame> frames = stackTrace.getFrames();
indent();
int i = 0;
while (i < frames.size() && i < getStackDepth()) {
RecordedFrame frame = frames.get(i);
if (frame.isJavaFrame()) {
printIndent();
printValue(frame, null, "");
println();
i++;
}
}
if (stackTrace.isTruncated() || i == getStackDepth()) {
printIndent();
println("...");
}
retract();
printIndent();
println("]");
}
public void print(RecordedObject struct, String postFix) {
println("{");
indent();
for (ValueDescriptor v : struct.getFields()) {
printFieldValue(struct, v);
}
retract();
printIndent();
println("}" + postFix);
}
private void printFieldValue(RecordedObject struct, ValueDescriptor v) {
printIndent();
print(v.getName(), " = ");
printValue(getValue(struct, v), v, "");
}
private void printArray(Object[] array) {
println("[");
indent();
for (int i = 0; i < array.length; i++) {
printIndent();
printValue(array[i], null, i + 1 < array.length ? ", " : "");
}
retract();
printIndent();
println("]");
}
private void printValue(Object value, ValueDescriptor field, String postFix) {
if (value == null) {
println("N/A" + postFix);
return;
}
if (value instanceof RecordedObject) {
if (value instanceof RecordedThread) {
printThread((RecordedThread) value, postFix);
return;
}
if (value instanceof RecordedClass) {
printClass((RecordedClass) value, postFix);
return;
}
if (value instanceof RecordedClassLoader) {
printClassLoader((RecordedClassLoader) value, postFix);
return;
}
if (value instanceof RecordedFrame) {
RecordedFrame frame = (RecordedFrame) value;
if (frame.isJavaFrame()) {
printJavaFrame((RecordedFrame) value, postFix);
return;
}
}
if (value instanceof RecordedMethod) {
println(formatMethod((RecordedMethod) value));
return;
}
if (field.getTypeName().equals(TYPE_OLD_OBJECT)) {
printOldObject((RecordedObject) value);
return;
}
print((RecordedObject) value, postFix);
return;
}
if (value.getClass().isArray()) {
printArray((Object[]) value);
return;
}
if (value instanceof Double) {
Double d = (Double) value;
if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) {
println("N/A");
return;
}
}
if (value instanceof Float) {
Float f = (Float) value;
if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) {
println("N/A");
return;
}
}
if (value instanceof Long) {
Long l = (Long) value;
if (l == Long.MIN_VALUE) {
println("N/A");
return;
}
}
if (value instanceof Integer) {
Integer i = (Integer) value;
if (i == Integer.MIN_VALUE) {
println("N/A");
return;
}
}
if (field.getContentType() != null) {
if (printFormatted(field, value)) {
return;
}
}
String text = String.valueOf(value);
if (value instanceof String) {
text = "\"" + text + "\"";
}
println(text);
}
private void printOldObject(RecordedObject object) {
println(" [");
indent();
printIndent();
try {
printReferenceChain(object);
} catch (IllegalArgumentException iae) {
// Could not find a field
// Not possible to validate fields beforehand using RecordedObject#hasField
// since nested objects, for example object.referrer.array.index, requires
// an actual array object (which may be null).
}
retract();
printIndent();
println("]");
}
private void printReferenceChain(RecordedObject object) {
printObject(object, currentEvent.getLong("arrayElements"));
for (RecordedObject ref = object.getValue("referrer"); ref != null; ref = object.getValue("referrer")) {
long skip = ref.getLong("skip");
if (skip > 0) {
printIndent();
println("...");
}
String objectHolder = "";
long size = Long.MIN_VALUE;
RecordedObject array = ref.getValue("array");
if (array != null) {
long index = array.getLong("index");
size = array.getLong("size");
objectHolder = "[" + index + "]";
}
RecordedObject field = ref.getValue("field");
if (field != null) {
objectHolder = field.getString("name");
}
printIndent();
print(objectHolder);
print(" : ");
object = ref.getValue("object");
if (object != null) {
printObject(object, size);
}
}
}
void printObject(RecordedObject object, long arraySize) {
RecordedClass clazz = object.getClass("type");
if (clazz != null) {
String className = clazz.getName();
if (className!= null && className.startsWith("[")) {
className = decodeDescriptors(className, arraySize > 0 ? Long.toString(arraySize) : "").get(0);
}
print(className);
String description = object.getString("description");
if (description != null) {
print(" ");
print(description);
}
}
println();
}
private void printClassLoader(RecordedClassLoader cl, String postFix) {
// Purposely not printing class loader name to avoid cluttered output
RecordedClass clazz = cl.getType();
print(clazz == null ? "null" : clazz.getName());
if (clazz != null) {
print(" (");
print("id = ");
print(String.valueOf(cl.getId()));
println(")");
}
}
private void printJavaFrame(RecordedFrame f, String postFix) {
print(formatMethod(f.getMethod()));
int line = f.getLineNumber();
if (line >= 0) {
print(" line: " + line);
}
print(postFix);
}
private String formatMethod(RecordedMethod m) {
StringBuilder sb = new StringBuilder();
sb.append(m.getType().getName());
sb.append(".");
sb.append(m.getName());
sb.append("(");
StringJoiner sj = new StringJoiner(", ");
String md = m.getDescriptor().replace("/", ".");
String parameter = md.substring(1, md.lastIndexOf(")"));
for (String qualifiedName : decodeDescriptors(parameter, "")) {
String typeName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
sj.add(typeName);
}
sb.append(sj);
sb.append(")");
return sb.toString();
}
private void printClass(RecordedClass clazz, String postFix) {
RecordedClassLoader classLoader = clazz.getClassLoader();
String classLoaderName = "null";
if (classLoader != null) {
if (classLoader.getName() != null) {
classLoaderName = classLoader.getName();
} else {
classLoaderName = classLoader.getType().getName();
}
}
String className = clazz.getName();
if (className.startsWith("[")) {
className = decodeDescriptors(className, "").get(0);
}
println(className + " (classLoader = " + classLoaderName + ")" + postFix);
}
List<String> decodeDescriptors(String descriptor, String arraySize) {
List<String> descriptors = new ArrayList<>();
for (int index = 0; index < descriptor.length(); index++) {
String arrayBrackets = "";
while (descriptor.charAt(index) == '[') {
arrayBrackets = arrayBrackets + "[" + arraySize + "]" ;
arraySize = "";
index++;
}
char c = descriptor.charAt(index);
String type;
switch (c) {
case 'L':
int endIndex = descriptor.indexOf(';', index);
type = descriptor.substring(index + 1, endIndex);
index = endIndex;
break;
case 'I':
type = "int";
break;
case 'J':
type = "long";
break;
case 'Z':
type = "boolean";
break;
case 'D':
type = "double";
break;
case 'F':
type = "float";
break;
case 'S':
type = "short";
break;
case 'C':
type = "char";
break;
case 'B':
type = "byte";
break;
default:
type = "<unknown-descriptor-type>";
}
descriptors.add(type + arrayBrackets);
}
return descriptors;
}
private void printThread(RecordedThread thread, String postFix) {
long javaThreadId = thread.getJavaThreadId();
if (javaThreadId > 0) {
println("\"" + thread.getJavaName() + "\" (javaThreadId = " + thread.getJavaThreadId() + ")" + postFix);
} else {
println("\"" + thread.getOSName() + "\" (osThreadId = " + thread.getOSThreadId() + ")" + postFix);
}
}
private boolean printFormatted(ValueDescriptor field, Object value) {
if (value instanceof Duration) {
Duration d = (Duration) value;
if (d.getSeconds() == Long.MIN_VALUE && d.getNano() == 0) {
println("N/A");
return true;
}
double s = d.getNano() / 1000_000_000.0 + (int) (d.getSeconds() % 60);
if (s < 1.0) {
if (s < 0.001) {
println(String.format("%.3f", s * 1_000_000) + " us");
} else {
println(String.format("%.3f", s * 1_000) + " ms");
}
} else {
if (s < 1000.0) {
println(String.format("%.3f", s) + " s");
} else {
println(String.format("%.0f", s) + " s");
}
}
return true;
}
if (value instanceof OffsetDateTime) {
OffsetDateTime odt = (OffsetDateTime) value;
if (odt.equals(OffsetDateTime.MIN)) {
println("N/A");
return true;
}
println(TIME_FORMAT.format(odt));
return true;
}
Percentage percentage = field.getAnnotation(Percentage.class);
if (percentage != null) {
if (value instanceof Number) {
double d = ((Number) value).doubleValue();
println(String.format("%.2f", d * 100) + "%");
return true;
}
}
DataAmount dataAmount = field.getAnnotation(DataAmount.class);
if (dataAmount != null) {
if (value instanceof Number) {
Number n = (Number) value;
long amount = n.longValue();
if (field.getAnnotation(Frequency.class) != null) {
if (dataAmount.value().equals(DataAmount.BYTES)) {
println(Utils.formatBytesPerSecond(amount));
return true;
}
if (dataAmount.value().equals(DataAmount.BITS)) {
println(Utils.formatBitsPerSecond(amount));
return true;
}
} else {
if (dataAmount.value().equals(DataAmount.BYTES)) {
println(Utils.formatBytes(amount));
return true;
}
if (dataAmount.value().equals(DataAmount.BITS)) {
println(Utils.formatBits(amount));
return true;
}
}
}
}
MemoryAddress memoryAddress = field.getAnnotation(MemoryAddress.class);
if (memoryAddress != null) {
if (value instanceof Number) {
long d = ((Number) value).longValue();
println(String.format("0x%08X", d));
return true;
}
}
Frequency frequency = field.getAnnotation(Frequency.class);
if (frequency != null) {
if (value instanceof Number) {
println(value + " Hz");
return true;
}
}
return false;
}
public void setShowIds(boolean showIds) {
this.showIds = showIds;
}
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import jdk.jfr.EventType;
final class Print extends Command {
@Override
public String getName() {
return "print";
}
@Override
public List<String> getOptionSyntax() {
List<String> list = new ArrayList<>();
list.add("[--xml|--json]");
list.add("[--categories <filter>]");
list.add("[--events <filter>]");
list.add("[--stack-depth <depth>]");
list.add("<file>");
return list;
}
@Override
protected String getTitle() {
return "Print contents of a recording file";
}
@Override
public String getDescription() {
return getTitle() + ". See 'jfr help print' for details.";
}
@Override
public void displayOptionUsage(PrintStream stream) {
stream.println(" --xml Print recording in XML format");
stream.println();
stream.println(" --json Print recording in JSON format");
stream.println();
stream.println(" --categories <filter> Select events matching a category name.");
stream.println(" The filter is a comma-separated list of names,");
stream.println(" simple and/or qualified, and/or quoted glob patterns");
stream.println();
stream.println(" --events <filter> Select events matching an event name.");
stream.println(" The filter is a comma-separated list of names,");
stream.println(" simple and/or qualified, and/or quoted glob patterns");
stream.println();
stream.println(" --stack-depth <depth> Number of frames in stack traces, by default 5");
stream.println();
stream.println(" <file> Location of the recording file (.jfr)");
stream.println();
stream.println();
stream.println("Example usage:");
stream.println();
stream.println(" jfr print --events OldObjectSample recording.jfr");
stream.println();
stream.println(" jfr print --events CPULoad,GarbageCollection recording.jfr");
stream.println();
stream.println(" jfr print --categories \"GC,JVM,Java*\" recording.jfr");
stream.println();
stream.println(" jfr print --events \"jdk.*\" --stack-depth 64 recording.jfr");
stream.println();
stream.println(" jfr print --json --events CPULoad recording.jfr");
}
@Override
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
Path file = getJFRInputFile(options);
PrintWriter pw = new PrintWriter(System.out, false);
Predicate<EventType> eventFilter = null;
int stackDepth = 5;
EventPrintWriter eventWriter = null;
int optionCount = options.size();
boolean foundEventFilter = false;
boolean foundCategoryFilter = false;
while (optionCount > 0) {
if (acceptFilterOption(options, "--events")) {
if (foundEventFilter) {
throw new UserSyntaxException("use --events event1,event2,event3 to include multiple events");
}
foundEventFilter = true;
String filter = options.remove();
warnForWildcardExpansion("--events", filter);
eventFilter = addEventFilter(filter, eventFilter);
}
if (acceptFilterOption(options, "--categories")) {
if (foundCategoryFilter) {
throw new UserSyntaxException("use --categories category1,category2 to include multiple categories");
}
foundCategoryFilter = true;
String filter = options.remove();
warnForWildcardExpansion("--categories", filter);
eventFilter = addCategoryFilter(filter, eventFilter);
}
if (acceptOption(options, "--stack-depth")) {
String value = options.pop();
try {
stackDepth = Integer.parseInt(value);
if (stackDepth < 0) {
throw new UserSyntaxException("stack depth must be zero or a positive integer.");
}
} catch (NumberFormatException nfe) {
throw new UserSyntaxException("not a valid value for --stack-depth");
}
}
if (acceptFormatterOption(options, eventWriter, "--json")) {
eventWriter = new JSONWriter(pw);
}
if (acceptFormatterOption(options, eventWriter, "--xml")) {
eventWriter = new XMLWriter(pw);
}
if (optionCount == options.size()) {
// No progress made
checkCommonError(options, "--event", "--events");
checkCommonError(options, "--category", "--categories");
throw new UserSyntaxException("unknown option " + options.peek());
}
optionCount = options.size();
}
if (eventWriter == null) {
eventWriter = new PrettyWriter(pw); // default to pretty printer
}
eventWriter.setStackDepth(stackDepth);
if (eventFilter != null) {
eventFilter = addCache(eventFilter, eventType -> eventType.getId());
eventWriter.setEventFilter(eventFilter);
}
try {
eventWriter.print(file);
} catch (IOException ioe) {
couldNotReadError(file, ioe);
}
pw.flush();
}
private void checkCommonError(Deque<String> options, String typo, String correct) throws UserSyntaxException {
if (typo.equals(options.peek())) {
throw new UserSyntaxException("unknown option " + typo + ", did you mean " + correct + "?");
}
}
private static boolean acceptFormatterOption(Deque<String> options, EventPrintWriter eventWriter, String expected) throws UserSyntaxException {
if (expected.equals(options.peek())) {
if (eventWriter != null) {
throw new UserSyntaxException("only one format can be specified at a time");
}
options.remove();
return true;
}
return false;
}
private static <T, X> Predicate<T> addCache(final Predicate<T> filter, Function<T, X> cacheFunction) {
Map<X, Boolean> cache = new HashMap<>();
return t -> cache.computeIfAbsent(cacheFunction.apply(t), x -> filter.test(t));
}
private static <T> Predicate<T> recurseIfPossible(Predicate<T> filter) {
return x -> filter != null && filter.test(x);
}
private static Predicate<EventType> addCategoryFilter(String filterText, Predicate<EventType> eventFilter) throws UserSyntaxException {
List<String> filters = explodeFilter(filterText);
Predicate<EventType> newFilter = recurseIfPossible(eventType -> {
for (String category : eventType.getCategoryNames()) {
for (String filter : filters) {
if (match(category, filter)) {
return true;
}
if (category.contains(" ") && acronomify(category).equals(filter)) {
return true;
}
}
}
return false;
});
return eventFilter == null ? newFilter : eventFilter.or(newFilter);
}
private static String acronomify(String multipleWords) {
boolean newWord = true;
String acronym = "";
for (char c : multipleWords.toCharArray()) {
if (newWord) {
if (Character.isAlphabetic(c) && Character.isUpperCase(c)) {
acronym += c;
}
}
newWord = Character.isWhitespace(c);
}
return acronym;
}
private static Predicate<EventType> addEventFilter(String filterText, final Predicate<EventType> eventFilter) throws UserSyntaxException {
List<String> filters = explodeFilter(filterText);
Predicate<EventType> newFilter = recurseIfPossible(eventType -> {
for (String filter : filters) {
String fullEventName = eventType.getName();
if (match(fullEventName, filter)) {
return true;
}
String eventName = fullEventName.substring(fullEventName.lastIndexOf(".") + 1);
if (match(eventName, filter)) {
return true;
}
}
return false;
});
return eventFilter == null ? newFilter : eventFilter.or(newFilter);
}
private static boolean match(String text, String filter) {
if (filter.length() == 0) {
// empty filter string matches if string is empty
return text.length() == 0;
}
if (filter.charAt(0) == '*') { // recursive check
filter = filter.substring(1);
for (int n = 0; n <= text.length(); n++) {
if (match(text.substring(n), filter))
return true;
}
} else if (text.length() == 0) {
// empty string and non-empty filter does not match
return false;
} else if (filter.charAt(0) == '?') {
// eat any char and move on
return match(text.substring(1), filter.substring(1));
} else if (filter.charAt(0) == text.charAt(0)) {
// eat chars and move on
return match(text.substring(1), filter.substring(1));
}
return false;
}
private static List<String> explodeFilter(String filter) throws UserSyntaxException {
List<String> list = new ArrayList<>();
for (String s : filter.split(",")) {
s = s.trim();
if (!s.isEmpty()) {
list.add(s);
}
}
return list;
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.PrintWriter;
abstract class StructuredWriter {
private final static String LINE_SEPARATOR = String.format("%n");
private final PrintWriter out;
private final StringBuilder builder = new StringBuilder(4000);
private char[] indentionArray = new char[0];
private int indent = 0;
private int column;
// print first event immediately so tool feels responsive
private boolean first = true;
StructuredWriter(PrintWriter p) {
out = p;
}
final protected int getColumn() {
return column;
}
// Flush to print writer
public final void flush(boolean hard) {
if (hard) {
out.print(builder.toString());
builder.setLength(0);
return;
}
if (first || builder.length() > 100_000) {
out.print(builder.toString());
builder.setLength(0);
first = false;
}
}
final public void printIndent() {
builder.append(indentionArray, 0, indent);
column += indent;
}
final public void println() {
builder.append(LINE_SEPARATOR);
column = 0;
}
final public void print(String... texts) {
for (String text : texts) {
print(text);
}
}
final public void printAsString(Object o) {
print(String.valueOf(o));
}
final public void print(String text) {
builder.append(text);
column += text.length();
}
final public void print(char c) {
builder.append(c);
column++;
}
final public void print(int value) {
print(String.valueOf(value));
}
final public void indent() {
indent += 2;
updateIndent();
}
final public void retract() {
indent -= 2;
updateIndent();
}
final public void println(String text) {
print(text);
println();
}
private void updateIndent() {
if (indent > indentionArray.length) {
indentionArray = new char[indent];
for (int i = 0; i < indentionArray.length; i++) {
indentionArray[i] = ' ';
}
}
}
}

View 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();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
/**
* Exception that is thrown if there is something wrong with the input, for instance
* a file that can't be read or a numerical value that is out of range.
* <p>
* When this exception is thrown, a user will typically not want to see the
* command line syntax, but instead information about what was wrong with the
* input.
*/
final class UserDataException extends Exception {
private static final long serialVersionUID = 6656457380115167810L;
/**
* The error message.
*
* The first letter should not be capitalized, so a context can be printed prior
* to the error message.
*
* @param errorMessage
*/
public UserDataException(String errorMessage) {
super(errorMessage);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
/**
* Exception that is thrown if options don't follow the syntax of the command.
*/
final class UserSyntaxException extends Exception {
private static final long serialVersionUID = 3437009454344160933L;
/**
* The error message.
*
* The first letter should not be capitalized, so a context can be printed prior
* to the error message.
*
* @param errorMessage
*/
public UserSyntaxException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
final class Version extends Command {
@Override
public String getName() {
return "version";
}
@Override
public String getDescription() {
return "Display version of the jfr tool";
}
@Override
public void execute(Deque<String> options) {
System.out.println("1.0");
}
protected List<String> getAliases() {
return Arrays.asList("--version");
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.tool;
import java.io.PrintWriter;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame;
import jdk.jfr.consumer.RecordedObject;
final class XMLWriter extends EventPrintWriter {
public XMLWriter(PrintWriter destination) {
super(destination);
}
@Override
protected void printBegin() {
println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
println("<recording xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
indent();
printIndent();
println("<events>");
indent();
}
@Override
protected void printEnd() {
retract();
printIndent();
println("</events>");
retract();
println("</recording>");
}
@Override
protected void print(List<RecordedEvent> events) {
for (RecordedEvent event : events) {
printEvent(event);
}
}
private void printEvent(RecordedEvent event) {
EventType type = event.getEventType();
printIndent();
print("<event");
printAttribute("type", type.getName());
print(">");
println();
indent();
for (ValueDescriptor v : event.getFields()) {
printValueDescriptor(v, getValue(event, v), -1);
}
retract();
printIndent();
println("</event>");
println();
}
private void printAttribute(String name, String value) {
print(" ");
print(name); // Only known strings
print("=\"");
printEscaped(value);
print("\"");
}
public void printObject(RecordedObject struct) {
println();
indent();
for (ValueDescriptor v : struct.getFields()) {
printValueDescriptor(v, getValue(struct, v), -1);
}
retract();
}
private void printArray(ValueDescriptor v, Object[] array) {
println();
indent();
int depth = 0;
for (int index = 0; index < array.length; index++) {
Object arrayElement = array[index];
if (!(arrayElement instanceof RecordedFrame) || depth < getStackDepth()) {
printValueDescriptor(v, array[index], index);
}
depth++;
}
retract();
}
private void printValueDescriptor(ValueDescriptor vd, Object value, int index) {
boolean arrayElement = index != -1;
String name = arrayElement ? null : vd.getName();
if (vd.isArray() && !arrayElement) {
if (printBeginElement("array", name, value, index)) {
printArray(vd, (Object[]) value);
printIndent();
printEndElement("array");
}
return;
}
if (!vd.getFields().isEmpty()) {
if (printBeginElement("struct", name, value, index)) {
printObject((RecordedObject) value);
printIndent();
printEndElement("struct");
}
return;
}
if (printBeginElement("value", name, value, index)) {
printEscaped(String.valueOf(value));
printEndElement("value");
}
}
private boolean printBeginElement(String elementName, String name, Object value, int index) {
printIndent();
print("<", elementName);
if (name != null) {
printAttribute("name", name);
}
if (index != -1) {
printAttribute("index", Integer.toString(index));
}
if (value == null) {
printAttribute("xsi:nil", "true");
println("/>");
return false;
}
if (value.getClass().isArray()) {
Object[] array = (Object[]) value;
printAttribute("size", Integer.toString(array.length));
}
print(">");
return true;
}
private void printEndElement(String elementName) {
print("</");
print(elementName);
println(">");
}
private void printEscaped(String text) {
for (int i = 0; i < text.length(); i++) {
printEscaped(text.charAt(i));
}
}
private void printEscaped(char c) {
if (c == 34) {
print("&quot;");
return;
}
if (c == 38) {
print("&amp;");
return;
}
if (c == 39) {
print("&apos;");
return;
}
if (c == 60) {
print("&lt;");
return;
}
if (c == 62) {
print("&gt;");
return;
}
if (c > 0x7F) {
print("&#");
print((int) c);
print(';');
return;
}
print(c);
}
}