diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Aggregator.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Aggregator.java index 1e02a97a4b9..cc1065f25b6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Aggregator.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Aggregator.java @@ -56,6 +56,10 @@ enum Aggregator { * Aggregate values into a comma-separated list, including {@code null}. */ LIST("LIST"), + /** + * Aggregate unique values into a comma-separated list, including {@code null}. + */ + SET("SET"), /** * The highest numeric value. */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java index 454ec180530..d91fa62b368 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldBuilder.java @@ -28,7 +28,6 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -122,7 +121,10 @@ final class FieldBuilder { configureNotInitFrameField(); return true; } - + if (fieldName.equals("stackTrace.topFrame.class")) { + configureTopFrameClassField(); + return true; + } if (fieldName.equals("stackTrace.topFrame")) { configureTopFrameField(); return true; @@ -175,6 +177,20 @@ final class FieldBuilder { field.lexicalSort = true; } + private void configureTopFrameClassField() { + field.alignLeft = true; + field.label = "Class"; + field.dataType = "java.lang.Class"; + field.valueGetter = e -> { + RecordedStackTrace t = e.getStackTrace(); + if (t == null) { + return null; + } + return t.getFrames().getFirst().getMethod().getType(); + }; + field.lexicalSort = true; + } + private void configureCustomFrame(Predicate condition) { field.alignLeft = true; field.dataType = "jdk.types.Frame"; @@ -379,7 +395,7 @@ final class FieldBuilder { field.alignLeft = false; field.lexicalSort = false; } - if (aggregator == Aggregator.LIST) { + if (aggregator == Aggregator.LIST || aggregator == Aggregator.SET) { field.alignLeft = true; field.lexicalSort = true; } @@ -392,6 +408,7 @@ final class FieldBuilder { case SUM -> "Total " + field.label; case UNIQUE -> "Unique Count " + field.label; case LIST -> field.label + "s"; + case SET -> field.label + "s"; case MISSING -> field.label; case DIFFERENCE -> "Difference " + field.label; case MEDIAN -> "Median " + field.label; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java index ccb066b2301..117dc6d01f3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/FieldFormatter.java @@ -27,6 +27,8 @@ package jdk.jfr.internal.query; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.StringJoiner; import jdk.jfr.consumer.RecordedClass; import jdk.jfr.consumer.RecordedClassLoader; @@ -51,6 +53,13 @@ public class FieldFormatter { if (object == null) { return field.missingText; } + if (object instanceof Collection c) { + StringJoiner sj = new StringJoiner(", "); + for (Object o : c) { + sj.add(format(field, o, compact)); + } + return sj.toString(); + } if (object instanceof String s) { return stripFormatting(s); } @@ -71,6 +80,10 @@ public class FieldFormatter { return field.missingText; } + if (object instanceof RecordedFrame f && f.isJavaFrame()) { + object = f.getMethod(); + } + if (object instanceof RecordedThread t) { if (t.getJavaThreadId() > 0) { return t.getJavaName(); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java index c3146117141..2cd1fd82825 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java @@ -28,10 +28,10 @@ import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; -import java.util.StringJoiner; abstract class Function { @@ -46,7 +46,11 @@ abstract class Function { return new FirstNonNull(); } if (aggregator == Aggregator.LIST) { - return new List(); + return new Container(new ArrayList<>()); + } + + if (aggregator == Aggregator.SET) { + return new Container(new LinkedHashSet<>()); } if (aggregator == Aggregator.DIFFERENCE) { @@ -378,7 +382,7 @@ abstract class Function { // **** UNIQUE **** private static final class Unique extends Function { - private final Set unique = new HashSet<>(); + private final Set unique = new LinkedHashSet<>(); @Override public void add(Object value) { @@ -391,23 +395,22 @@ abstract class Function { } } - // **** LIST **** + // **** LIST and SET **** - private static final class List extends Function { - private final ArrayList list = new ArrayList<>(); + private static final class Container extends Function { + private final Collection collection; + private Container(Collection collection) { + this.collection = collection; + } @Override public void add(Object value) { - list.add(value); + collection.add(value); } @Override public Object result() { - StringJoiner sj = new StringJoiner(", "); - for (Object object : list) { - sj.add(String.valueOf(object)); - } - return sj.toString(); + return collection; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableCell.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableCell.java index 1099aa866f4..7928eae2f4c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableCell.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableCell.java @@ -25,6 +25,7 @@ package jdk.jfr.internal.query; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import jdk.jfr.internal.query.Configuration.Truncate; @@ -69,7 +70,7 @@ final class TableCell { } public void addLine(String text) { int contentWidth = getContentWidth(); - if (text.length() >= contentWidth) { + if (text.length() > contentWidth) { add(truncate(text, contentWidth)); } else { addAligned(text); @@ -143,6 +144,10 @@ final class TableCell { } } + public void sort() { + Collections.sort(lines); + } + public void clear() { lines.clear(); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableRenderer.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableRenderer.java index dd9497d344f..6b99ce90efc 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableRenderer.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/TableRenderer.java @@ -28,11 +28,10 @@ import static jdk.jfr.internal.query.Configuration.MAX_PREFERRED_WIDTH; import static jdk.jfr.internal.query.Configuration.MIN_PREFERRED_WIDTH; import static jdk.jfr.internal.query.Configuration.PREFERRED_WIDTH; +import java.util.Collection; import java.util.List; import java.util.function.Predicate; -import jdk.jfr.consumer.RecordedFrame; -import jdk.jfr.consumer.RecordedMethod; import jdk.jfr.consumer.RecordedStackTrace; import jdk.jfr.internal.query.Configuration.Truncate; import jdk.jfr.internal.util.Output; @@ -293,37 +292,39 @@ final class TableRenderer { if (cell.cellHeight > 1) { Object o = row.getValue(columnIndex); if (o instanceof RecordedStackTrace s) { - setStackTrace(cell, s); + o = s.getFrames(); + } + if (o instanceof Collection c) { + setMultiline(cell, c); return; } } if (text.length() > cell.getContentSize()) { Object o = row.getValue(columnIndex); - cell.setContent(FieldFormatter.formatCompact(cell.field, o)); return; } cell.setContent(text); } - private void setStackTrace(TableCell cell, RecordedStackTrace s) { + private void setMultiline(TableCell cell, Collection objects) { int row = 0; cell.clear(); - for(RecordedFrame f : s.getFrames()) { + for(Object object : objects) { if (row == cell.cellHeight) { return; } - if (f.isJavaFrame()) { - RecordedMethod method = f.getMethod(); - String text = FieldFormatter.format(cell.field, method); - if (text.length() > cell.getContentWidth()) { - text = FieldFormatter.formatCompact(cell.field, method); - } - cell.addLine(text); + String text = FieldFormatter.format(cell.field, object); + if (text.length() > cell.getContentWidth()) { + text = FieldFormatter.formatCompact(cell.field, object); } + cell.addLine(text); row++; } + if (cell.field.lexicalSort) { + cell.sort(); + } } private void printRow() { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini index 615588a88ca..6b5b07fd242 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini @@ -173,6 +173,16 @@ table = "COLUMN 'Monitor Address', 'Class', 'Threads', 'Max Duration' FROM JavaMonitorEnter GROUP BY monitorClass ORDER BY M" +[application.deprecated-methods-for-removal] +label = "Deprecated Methods for Removal" +table = "COLUMN 'Deprecated Method', 'Called from Class' + FORMAT truncate-beginning, cell-height:10000;truncate-beginning + SELECT method AS m, SET(stackTrace.topFrame.class) + FROM DeprecatedInvocation + WHERE forRemoval = 'true' + GROUP BY m + ORDER BY m" + [environment.cpu-information] label ="CPU Information" form = "SELECT cpu, sockets, cores, hwThreads, description FROM CPUInformation"