< prev index next >

application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/ThreadsPage.java

Print this page




  59 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  60 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  61 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  62 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics;
  63 import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI;
  64 import org.openjdk.jmc.flightrecorder.ui.IDataPageFactory;
  65 import org.openjdk.jmc.flightrecorder.ui.IDisplayablePage;
  66 import org.openjdk.jmc.flightrecorder.ui.IPageContainer;
  67 import org.openjdk.jmc.flightrecorder.ui.IPageDefinition;
  68 import org.openjdk.jmc.flightrecorder.ui.IPageUI;
  69 import org.openjdk.jmc.flightrecorder.ui.StreamModel;
  70 import org.openjdk.jmc.flightrecorder.ui.common.AbstractDataPage;
  71 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector.FlavorSelectorState;
  72 import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants;
  73 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram;
  74 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.HistogramSelection;
  75 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.ItemHistogramBuilder;
  76 import org.openjdk.jmc.flightrecorder.ui.common.ItemRow;
  77 import org.openjdk.jmc.flightrecorder.ui.common.ThreadGraphLanes;
  78 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;

  79 import org.openjdk.jmc.ui.charts.IXDataRenderer;

  80 import org.openjdk.jmc.ui.charts.RendererToolkit;
  81 import org.openjdk.jmc.ui.column.ColumnManager.SelectionState;
  82 import org.openjdk.jmc.ui.column.TableSettings;
  83 import org.openjdk.jmc.ui.handlers.ActionToolkit;
  84 import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
  85 
  86 public class ThreadsPage extends AbstractDataPage {
  87 
  88         public static class ThreadsPageFactory implements IDataPageFactory {
  89 
  90                 @Override
  91                 public String getName(IState state) {
  92                         return Messages.ThreadsPage_NAME;
  93                 }
  94 
  95                 @Override
  96                 public String[] getTopics(IState state) {
  97                         return new String[] {JfrRuleTopics.THREADS_TOPIC};
  98                 }
  99 


 135                  * to all other columns.
 136                  */
 137                 HISTOGRAM.addColumn(THREAD_END_COL,
 138                                 max(Messages.JavaApplicationPage_COLUMN_THREAD_END, Messages.JavaApplicationPage_COLUMN_THREAD_END_DESC,
 139                                                 JdkTypeIDs.JAVA_THREAD_END, JfrAttributes.EVENT_TIMESTAMP));
 140                 HISTOGRAM.addColumn(THREAD_DURATION_COL, ic -> {
 141                         IQuantity threadStart = ic.apply(ItemFilters.type(JdkTypeIDs.JAVA_THREAD_START))
 142                                         .getAggregate((IAggregator<IQuantity, ?>) Aggregators.min(JfrAttributes.EVENT_TIMESTAMP));
 143                         IQuantity threadEnd = ic.apply(ItemFilters.type(JdkTypeIDs.JAVA_THREAD_END))
 144                                         .getAggregate((IAggregator<IQuantity, ?>) Aggregators.max(JfrAttributes.EVENT_TIMESTAMP));
 145                         if (threadStart != null && threadEnd != null) {
 146                                 return threadEnd.subtract(threadStart);
 147                         }
 148                         return null;
 149                 }, Messages.JavaApplicationPage_COLUMN_THREAD_DURATION,
 150                                 Messages.JavaApplicationPage_COLUMN_THREAD_DURATION_DESC);
 151         }
 152 
 153         private class ThreadsPageUi extends ChartAndTableUI {
 154                 private static final String THREADS_TABLE_FILTER = "threadsTableFilter"; //$NON-NLS-1$




 155                 private ThreadGraphLanes lanes;
 156                 private MCContextMenuManager mm;




 157 
 158                 ThreadsPageUi(Composite parent, FormToolkit toolkit, IPageContainer editor, IState state) {
 159                         super(pageFilter, getDataSource(), parent, toolkit, editor, state, getName(), pageFilter, getIcon(),
 160                                         flavorSelectorState);
 161                         mm = (MCContextMenuManager) chartCanvas.getContextMenu();
 162                         sash.setOrientation(SWT.HORIZONTAL);
 163                         mm.add(new Separator());
 164                         // FIXME: The lanes field is initialized by initializeChartConfiguration which is called by the super constructor. This is too indirect for SpotBugs to resolve and should be simplified.
 165                         lanes.updateContextMenu(mm, false);
 166 
 167                         form.getToolBarManager()
 168                                         .add(ActionToolkit.action(() -> lanes.openEditLanesDialog(mm, false), Messages.ThreadsPage_EDIT_LANES,
 169                                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES_EDIT)));
 170                         form.getToolBarManager().update(true);
 171                         chartLegend.getControl().dispose();
 172                         buildChart();
 173                         table.getManager().setSelectionState(histogramSelectionState);
 174                         tableFilterComponent.loadState(state.getChild(THREADS_TABLE_FILTER));
 175                         chart.setVisibleRange(visibleRange.getStart(), visibleRange.getEnd());
 176                         onFilterChange(tableFilter);
 177                 }
 178 
















































































 179                 @Override
 180                 protected ItemHistogram buildHistogram(Composite parent, IState state) {
 181                         ItemHistogram build = HISTOGRAM.buildWithoutBorder(parent, JfrAttributes.EVENT_THREAD,
 182                                         TableSettings.forState(state));
 183                         return build;
 184                 }
 185 
 186                 @Override
 187                 protected IXDataRenderer getChartRenderer(IItemCollection itemsInTable, HistogramSelection tableSelection) {
 188                         List<IXDataRenderer> rows = new ArrayList<>();
 189 
 190                         IItemCollection selectedItems;
 191                         HistogramSelection selection;
 192                         if (tableSelection.getRowCount() == 0) {
 193                                 selectedItems = itemsInTable;
 194                                 selection = table.getAllRows();
 195                         } else {
 196                                 selectedItems = tableSelection.getItems();
 197                                 selection = tableSelection;
 198                         }
 199                         boolean useDefaultSelection = rows.size() > 1;
 200                         if (lanes.getLaneDefinitions().stream().anyMatch(a -> a.isEnabled()) && selection.getRowCount() > 0) {
 201                                 List<IXDataRenderer> threadRows = selection

 202                                                 .getSelectedRows((object, items) -> lanes.buildThreadRenderer(object, items))
 203                                                 .collect(Collectors.toList());









 204                                 double threadsWeight = Math.sqrt(threadRows.size()) * 0.15;
 205                                 double otherRowWeight = Math.max(threadsWeight * 0.1, (1 - threadsWeight) / rows.size());
 206                                 List<Double> weights = Stream
 207                                                 .concat(Stream.generate(() -> otherRowWeight).limit(rows.size()), Stream.of(threadsWeight))
 208                                                 .collect(Collectors.toList());
 209                                 rows.add(RendererToolkit.uniformRows(threadRows));
 210                                 useDefaultSelection = true;
 211                                 rows = Arrays.asList(RendererToolkit.weightedRows(rows, weights));
 212                         }
 213                         IXDataRenderer root = rows.size() == 1 ? rows.get(0) : RendererToolkit.uniformRows(rows);
 214                         // We don't use the default selection when there is only one row. This is to get the correct payload.
 215                         return useDefaultSelection ? new ItemRow(root, selectedItems.apply(lanes.getEnabledLanesFilter())) : root;
 216                 }
 217 
 218                 @Override
 219                 protected void onFilterChange(IItemFilter filter) {
 220                         super.onFilterChange(filter);
 221                         tableFilter = filter;
 222                 }
 223 
 224                 @Override
 225                 public void saveTo(IWritableState state) {
 226                         super.saveTo(state);
 227                         tableFilterComponent.saveState(state.createChild(THREADS_TABLE_FILTER));
 228                         saveToLocal();
 229                 }
 230 
 231                 private void saveToLocal() {
 232                         flavorSelectorState = flavorSelector.getFlavorSelectorState();
 233                         histogramSelectionState = table.getManager().getSelectionState();
 234                         visibleRange = chart.getVisibleRange();
 235                 }
 236 
 237                 @Override
 238                 protected List<IAction> initializeChartConfiguration(IState state) {



 239                         lanes = new ThreadGraphLanes(() -> getDataSource(), () -> buildChart());
 240                         return lanes.initializeChartConfiguration(Stream.of(state.getChildren(THREAD_LANE)));
 241                 }
 242         }
 243 
 244         private FlavorSelectorState flavorSelectorState;
 245         private SelectionState histogramSelectionState;
 246         private IItemFilter tableFilter;
 247         private IRange<IQuantity> visibleRange;
 248 
 249         public ThreadsPage(IPageDefinition definition, StreamModel model, IPageContainer editor) {
 250                 super(definition, model, editor);
 251                 visibleRange = editor.getRecordingRange();
 252         }
 253 
 254         @Override
 255         public IPageUI display(Composite parent, FormToolkit toolkit, IPageContainer editor, IState state) {
 256                 return new ThreadsPageUi(parent, toolkit, editor, state);
 257         }
 258 


  59 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  60 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  61 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  62 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics;
  63 import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI;
  64 import org.openjdk.jmc.flightrecorder.ui.IDataPageFactory;
  65 import org.openjdk.jmc.flightrecorder.ui.IDisplayablePage;
  66 import org.openjdk.jmc.flightrecorder.ui.IPageContainer;
  67 import org.openjdk.jmc.flightrecorder.ui.IPageDefinition;
  68 import org.openjdk.jmc.flightrecorder.ui.IPageUI;
  69 import org.openjdk.jmc.flightrecorder.ui.StreamModel;
  70 import org.openjdk.jmc.flightrecorder.ui.common.AbstractDataPage;
  71 import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector.FlavorSelectorState;
  72 import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants;
  73 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram;
  74 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.HistogramSelection;
  75 import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.ItemHistogramBuilder;
  76 import org.openjdk.jmc.flightrecorder.ui.common.ItemRow;
  77 import org.openjdk.jmc.flightrecorder.ui.common.ThreadGraphLanes;
  78 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages;
  79 import org.openjdk.jmc.ui.UIPlugin;
  80 import org.openjdk.jmc.ui.charts.IXDataRenderer;
  81 import org.openjdk.jmc.ui.charts.QuantitySpanRenderer;
  82 import org.openjdk.jmc.ui.charts.RendererToolkit;
  83 import org.openjdk.jmc.ui.column.ColumnManager.SelectionState;
  84 import org.openjdk.jmc.ui.column.TableSettings;
  85 import org.openjdk.jmc.ui.handlers.ActionToolkit;
  86 import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
  87 
  88 public class ThreadsPage extends AbstractDataPage {
  89 
  90         public static class ThreadsPageFactory implements IDataPageFactory {
  91 
  92                 @Override
  93                 public String getName(IState state) {
  94                         return Messages.ThreadsPage_NAME;
  95                 }
  96 
  97                 @Override
  98                 public String[] getTopics(IState state) {
  99                         return new String[] {JfrRuleTopics.THREADS_TOPIC};
 100                 }
 101 


 137                  * to all other columns.
 138                  */
 139                 HISTOGRAM.addColumn(THREAD_END_COL,
 140                                 max(Messages.JavaApplicationPage_COLUMN_THREAD_END, Messages.JavaApplicationPage_COLUMN_THREAD_END_DESC,
 141                                                 JdkTypeIDs.JAVA_THREAD_END, JfrAttributes.EVENT_TIMESTAMP));
 142                 HISTOGRAM.addColumn(THREAD_DURATION_COL, ic -> {
 143                         IQuantity threadStart = ic.apply(ItemFilters.type(JdkTypeIDs.JAVA_THREAD_START))
 144                                         .getAggregate((IAggregator<IQuantity, ?>) Aggregators.min(JfrAttributes.EVENT_TIMESTAMP));
 145                         IQuantity threadEnd = ic.apply(ItemFilters.type(JdkTypeIDs.JAVA_THREAD_END))
 146                                         .getAggregate((IAggregator<IQuantity, ?>) Aggregators.max(JfrAttributes.EVENT_TIMESTAMP));
 147                         if (threadStart != null && threadEnd != null) {
 148                                 return threadEnd.subtract(threadStart);
 149                         }
 150                         return null;
 151                 }, Messages.JavaApplicationPage_COLUMN_THREAD_DURATION,
 152                                 Messages.JavaApplicationPage_COLUMN_THREAD_DURATION_DESC);
 153         }
 154 
 155         private class ThreadsPageUi extends ChartAndTableUI {
 156                 private static final String THREADS_TABLE_FILTER = "threadsTableFilter"; //$NON-NLS-1$
 157                 private static final String HIDE_THREAD = "hideThread"; //$NON-NLS-1$
 158                 private static final String RESET_CHART = "resetChart"; //$NON-NLS-1$
 159                 private IAction hideThreadAction;
 160                 private IAction resetChartAction;
 161                 private ThreadGraphLanes lanes;
 162                 private MCContextMenuManager mm;
 163                 private List<IXDataRenderer> threadRows;
 164                 private Boolean reloadThreads;
 165                 private Boolean isChartModified;
 166                 private Boolean isChartMenuActionsInit;
 167 
 168                 ThreadsPageUi(Composite parent, FormToolkit toolkit, IPageContainer editor, IState state) {
 169                         super(pageFilter, getDataSource(), parent, toolkit, editor, state, getName(), pageFilter, getIcon(),
 170                                         flavorSelectorState);
 171                         mm = (MCContextMenuManager) chartCanvas.getContextMenu();
 172                         sash.setOrientation(SWT.HORIZONTAL);
 173                         addActionsToContextMenu(mm);
 174                         // FIXME: The lanes field is initialized by initializeChartConfiguration which is called by the super constructor. This is too indirect for SpotBugs to resolve and should be simplified.
 175                         lanes.updateContextMenu(mm, false);
 176 
 177                         form.getToolBarManager()
 178                                         .add(ActionToolkit.action(() -> lanes.openEditLanesDialog(mm, false), Messages.ThreadsPage_EDIT_LANES,
 179                                                         FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES_EDIT)));
 180                         form.getToolBarManager().update(true);
 181                         chartLegend.getControl().dispose();
 182                         buildChart();
 183                         table.getManager().setSelectionState(histogramSelectionState);
 184                         tableFilterComponent.loadState(state.getChild(THREADS_TABLE_FILTER));
 185                         chart.setVisibleRange(visibleRange.getStart(), visibleRange.getEnd());
 186                         onFilterChange(tableFilter);
 187                 }
 188 
 189                 /**
 190                  * Hides a thread from the chart and rebuilds the chart
 191                  */
 192                 private void hideThread(String threadName) {
 193                         if (this.threadRows != null && this.threadRows.size() > 0) {
 194                                 int index = indexOfThreadName(threadName);
 195                                 if (index != -1) {
 196                                         this.threadRows.remove(index);
 197                                         this.reloadThreads = false;
 198                                         buildChart();
 199                                         if (!this.isChartModified) {
 200                                                 this.isChartModified = true;
 201                                                 setResetChartActionEnablement(true);
 202                                         }
 203                                 }
 204                                 if (this.threadRows.size() == 0) {
 205                                         setHideThreadActionEnablement(false);
 206                                 }
 207                         }
 208                 }
 209 
 210                 /**
 211                  * Locates the index of the target Thread (by name) in the current selection list
 212                  *
 213                  * @param name
 214                  *            the name of the Thread of interest
 215                  * @return the index of the Thread in the current selection, or -1 if not found
 216                  */
 217                 private int indexOfThreadName(String name) {
 218                         for (int i = 0; i < this.threadRows.size() && name != null; i++) {
 219                                 if (this.threadRows.get(i) instanceof QuantitySpanRenderer) {
 220                                         if (name.equals(((QuantitySpanRenderer) this.threadRows.get(i)).getName())) {
 221                                                 return i;
 222                                         }
 223                                 }
 224                         }
 225                         return -1;
 226                 }
 227 
 228                 /**
 229                  * Adds the hide thread and reset chart actions to the context menu
 230                  */
 231                 private void addActionsToContextMenu(MCContextMenuManager mm) {
 232                         mm.add(new Separator());
 233 
 234                         IAction hideThreadAction = ActionToolkit.action(() -> this.hideThread(chartCanvas.getActiveScopeName()),
 235                                         Messages.ThreadsPage_HIDE_THREAD_ACTION,
 236                                         UIPlugin.getDefault().getMCImageDescriptor(UIPlugin.ICON_DELETE));
 237                         hideThreadAction.setId(HIDE_THREAD);
 238                         this.hideThreadAction = hideThreadAction;
 239                         mm.add(hideThreadAction);
 240 
 241                         IAction resetChartAction = ActionToolkit.action(() -> this.resetChartToSelection(),
 242                                         Messages.ThreadsPage_RESET_CHART_TO_SELECTION_ACTION,
 243                                         UIPlugin.getDefault().getMCImageDescriptor(UIPlugin.ICON_REFRESH));
 244                         resetChartAction.setId(RESET_CHART);
 245                         resetChartAction.setEnabled(this.isChartModified);
 246                         this.resetChartAction = resetChartAction;
 247                         mm.add(resetChartAction);
 248 
 249                         this.isChartMenuActionsInit = true;
 250                 }
 251 
 252                 /**
 253                  * Redraws the chart, and disables the reset chart menu action
 254                  */
 255                 private void resetChartToSelection() {
 256                         buildChart();
 257                         this.isChartModified = false;
 258                         setResetChartActionEnablement(false);
 259                         setHideThreadActionEnablement(true);
 260                 }
 261 
 262                 private void setHideThreadActionEnablement(Boolean enabled) {
 263                         this.hideThreadAction.setEnabled(enabled);
 264                 }
 265                 private void setResetChartActionEnablement(Boolean enabled) {
 266                         this.resetChartAction.setEnabled(enabled);
 267                 }
 268 
 269                 @Override
 270                 protected ItemHistogram buildHistogram(Composite parent, IState state) {
 271                         ItemHistogram build = HISTOGRAM.buildWithoutBorder(parent, JfrAttributes.EVENT_THREAD,
 272                                         TableSettings.forState(state));
 273                         return build;
 274                 }
 275 
 276                 @Override
 277                 protected IXDataRenderer getChartRenderer(IItemCollection itemsInTable, HistogramSelection tableSelection) {
 278                         List<IXDataRenderer> rows = new ArrayList<>();
 279 
 280                         IItemCollection selectedItems;
 281                         HistogramSelection selection;
 282                         if (tableSelection.getRowCount() == 0) {
 283                                 selectedItems = itemsInTable;
 284                                 selection = table.getAllRows();
 285                         } else {
 286                                 selectedItems = tableSelection.getItems();
 287                                 selection = tableSelection;
 288                         }
 289                         boolean useDefaultSelection = rows.size() > 1;
 290                         if (lanes.getLaneDefinitions().stream().anyMatch(a -> a.isEnabled()) && selection.getRowCount() > 0) {
 291                                 if (this.reloadThreads) {
 292                                         this.threadRows = selection
 293                                                         .getSelectedRows((object, items) -> lanes.buildThreadRenderer(object, items))
 294                                                         .collect(Collectors.toList());
 295                                         this.isChartModified = false;
 296                                         if (this.isChartMenuActionsInit) {
 297                                                 setResetChartActionEnablement(false);
 298                                                 setHideThreadActionEnablement(true);
 299                                         }
 300                                 } else {
 301                                         this.reloadThreads = true;
 302                                 }
 303 
 304                                 double threadsWeight = Math.sqrt(threadRows.size()) * 0.15;
 305                                 double otherRowWeight = Math.max(threadsWeight * 0.1, (1 - threadsWeight) / rows.size());
 306                                 List<Double> weights = Stream
 307                                                 .concat(Stream.generate(() -> otherRowWeight).limit(rows.size()), Stream.of(threadsWeight))
 308                                                 .collect(Collectors.toList());
 309                                 rows.add(RendererToolkit.uniformRows(this.threadRows));
 310                                 useDefaultSelection = true;
 311                                 rows = Arrays.asList(RendererToolkit.weightedRows(rows, weights));
 312                         }
 313                         IXDataRenderer root = rows.size() == 1 ? rows.get(0) : RendererToolkit.uniformRows(rows);
 314                         // We don't use the default selection when there is only one row. This is to get the correct payload.
 315                         return useDefaultSelection ? new ItemRow(root, selectedItems.apply(lanes.getEnabledLanesFilter())) : root;
 316                 }
 317 
 318                 @Override
 319                 protected void onFilterChange(IItemFilter filter) {
 320                         super.onFilterChange(filter);
 321                         tableFilter = filter;
 322                 }
 323 
 324                 @Override
 325                 public void saveTo(IWritableState state) {
 326                         super.saveTo(state);
 327                         tableFilterComponent.saveState(state.createChild(THREADS_TABLE_FILTER));
 328                         saveToLocal();
 329                 }
 330 
 331                 private void saveToLocal() {
 332                         flavorSelectorState = flavorSelector.getFlavorSelectorState();
 333                         histogramSelectionState = table.getManager().getSelectionState();
 334                         visibleRange = chart.getVisibleRange();
 335                 }
 336 
 337                 @Override
 338                 protected List<IAction> initializeChartConfiguration(IState state) {
 339                         this.reloadThreads = true;
 340                         this.isChartModified = false;
 341                         this.isChartMenuActionsInit = false;
 342                         lanes = new ThreadGraphLanes(() -> getDataSource(), () -> buildChart());
 343                         return lanes.initializeChartConfiguration(Stream.of(state.getChildren(THREAD_LANE)));
 344                 }
 345         }
 346 
 347         private FlavorSelectorState flavorSelectorState;
 348         private SelectionState histogramSelectionState;
 349         private IItemFilter tableFilter;
 350         private IRange<IQuantity> visibleRange;
 351 
 352         public ThreadsPage(IPageDefinition definition, StreamModel model, IPageContainer editor) {
 353                 super(definition, model, editor);
 354                 visibleRange = editor.getRecordingRange();
 355         }
 356 
 357         @Override
 358         public IPageUI display(Composite parent, FormToolkit toolkit, IPageContainer editor, IState state) {
 359                 return new ThreadsPageUi(parent, toolkit, editor, state);
 360         }
 361 
< prev index next >