/* * Copyright (c) 2009, 2012, 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. * * 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 com.sun.max.ins.debug; import java.awt.*; import java.awt.event.*; import java.lang.ref.*; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; import com.sun.max.ins.*; import com.sun.max.ins.gui.*; import com.sun.max.ins.util.*; import com.sun.max.ins.value.*; import com.sun.max.ins.value.WordValueLabel.ValueMode; import com.sun.max.tele.*; import com.sun.max.vm.value.*; /** * A table specialized for displaying memory watchpoints in the VM. */ public final class WatchpointsTable extends InspectorTable { private final InspectorView view; private final WatchpointsTableModel tableModel; WatchpointsTable(Inspection inspection, InspectorView view, WatchpointsViewPreferences viewPreferences) { super(inspection); this.view = view; tableModel = new WatchpointsTableModel(inspection); WatchpointsColumnModel columnModel = new WatchpointsColumnModel(viewPreferences); configureDefaultTable(tableModel, columnModel); } WatchpointsColumnModel columnModel() { return (WatchpointsColumnModel) getColumnModel(); } @Override protected InspectorPopupMenu getPopupMenu(int row, int col, MouseEvent mouseEvent) { if (vm().watchpointManager() != null && col == WatchpointsColumnKind.DESCRIPTION.ordinal()) { final InspectorPopupMenu menu = new InspectorPopupMenu("Watchpoints"); final MaxWatchpoint watchpoint = (MaxWatchpoint) tableModel.getValueAt(row, col); final MaxObject object = watchpoint.getWatchedObject(); if (object != null) { menu.add(views().objects().makeViewAction(object, "View Object")); return menu; } } return null; } @Override public void updateFocusSelection() { // Sets table selection to the memory watchpoint, if any, that is the current user focus. final MaxWatchpoint watchpoint = focus().watchpoint(); final int row = tableModel.findRow(watchpoint); updateSelection(row); } @Override public void valueChanged(ListSelectionEvent listSelectionEvent) { // Row selection changed, perhaps by user mouse click or navigation; // update user focus to follow the selection. super.valueChanged(listSelectionEvent); if (!listSelectionEvent.getValueIsAdjusting()) { final int row = getSelectedRow(); if (row >= 0) { final MaxWatchpoint watchpoint = tableModel.rowToWatchpoint(row); focus().setWatchpoint(watchpoint); } } } /** * {@inheritDoc}. * <br> * Color the text specially in the row where a triggered watchpoint is displayed */ @Override public Color cellForegroundColor(int row, int col) { final MaxWatchpointEvent watchpointEvent = vm().state().watchpointEvent(); if (watchpointEvent != null && tableModel.rowToWatchpoint(row).memoryRegion().contains(watchpointEvent.address())) { return preference().style().debugIPTagColor(); } return null; } public InspectorView getView() { return view; } private final class WatchpointsColumnModel extends InspectorTableColumnModel<WatchpointsColumnKind> { private WatchpointsColumnModel(WatchpointsViewPreferences viewPreferences) { super(inspection(), WatchpointsColumnKind.values().length, viewPreferences); addColumnIfSupported(WatchpointsColumnKind.TAG, new TagCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.START, new StartAddressCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.SIZE, new SizeCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.END, new EndAddressCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.DESCRIPTION, new DescriptionCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.REGION, new RegionRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.READ, null, new DefaultCellEditor(new JCheckBox())); addColumnIfSupported(WatchpointsColumnKind.WRITE, null, new DefaultCellEditor(new JCheckBox())); addColumnIfSupported(WatchpointsColumnKind.EXEC, null, new DefaultCellEditor(new JCheckBox())); addColumnIfSupported(WatchpointsColumnKind.GC, null, new DefaultCellEditor(new JCheckBox())); addColumnIfSupported(WatchpointsColumnKind.TRIGGERED_THREAD, new TriggerThreadCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.ADDRESS_TRIGGERED, new TriggerAddressCellRenderer(inspection()), null); addColumnIfSupported(WatchpointsColumnKind.CODE_TRIGGERED, new TriggerCodeCellRenderer(inspection()), null); } } /** * A table data model built around the list of current watchpoints in the VM. * */ private final class WatchpointsTableModel extends InspectorTableModel { public WatchpointsTableModel(Inspection inspection) { super(inspection); } public int getColumnCount() { return WatchpointsColumnKind.values().length; } public int getRowCount() { return vm().watchpointManager().watchpoints().size(); } public Object getValueAt(int row, int col) { final MaxWatchpoint watchpoint = rowToWatchpoint(row); switch (WatchpointsColumnKind.values()[col]) { case TAG: case START: case SIZE: case END: case DESCRIPTION: case REGION: case TRIGGERED_THREAD: case ADDRESS_TRIGGERED: case CODE_TRIGGERED: return watchpoint; case READ: return watchpoint.getSettings().trapOnRead; case WRITE: return watchpoint.getSettings().trapOnWrite; case EXEC: return watchpoint.getSettings().trapOnExec; case GC: return watchpoint.getSettings().enabledDuringGC; default: throw InspectorError.unexpected("Unexpected Watchpoint Data column"); } } @Override public void setValueAt(Object value, int row, int column) { final MaxWatchpoint watchpoint = rowToWatchpoint(row); switch (WatchpointsColumnKind.values()[column]) { case READ: { final Boolean newState = (Boolean) value; try { if (watchpoint.setTrapOnRead(newState)) { inspection().settings().save(); } } catch (MaxVMBusyException maxVMBusyException) { final DefaultCellEditor editor = (DefaultCellEditor) columnModel().columnAt(column).getCellEditor(); final JCheckBox checkBox = (JCheckBox) editor.getComponent(); // System.out.println("Reset READ checkbox at row=" + row + ", col=" + column); checkBox.setSelected(!newState); inspection().announceVMBusyFailure("Watchpoint READ setting"); } break; } case WRITE: { final Boolean newState = (Boolean) value; try { if (watchpoint.setTrapOnWrite(newState)) { inspection().settings().save(); } } catch (MaxVMBusyException maxVMBusyException) { final DefaultCellEditor editor = (DefaultCellEditor) columnModel().columnAt(column).getCellEditor(); final JCheckBox checkBox = (JCheckBox) editor.getComponent(); // System.out.println("Reset WRITE checkbox at row=" + row + ", col=" + column); checkBox.setSelected(!newState); inspection().announceVMBusyFailure("Watchpoint WRITE setting"); } break; } case EXEC: { final Boolean newState = (Boolean) value; try { if (watchpoint.setTrapOnExec(newState)) { inspection().settings().save(); } } catch (MaxVMBusyException maxVMBusyException) { final DefaultCellEditor editor = (DefaultCellEditor) columnModel().columnAt(column).getCellEditor(); final JCheckBox checkBox = (JCheckBox) editor.getComponent(); // System.out.println("Reset EXEC checkbox at row=" + row + ", col=" + column); checkBox.setSelected(!newState); inspection().announceVMBusyFailure("Watchpoint EXEC setting"); } break; } case GC: { final Boolean newState = (Boolean) value; try { watchpoint.setEnabledDuringGC(newState); inspection().settings().save(); } catch (MaxVMBusyException maxVMBusyException) { final DefaultCellEditor editor = (DefaultCellEditor) columnModel().columnAt(column).getCellEditor(); final JCheckBox checkBox = (JCheckBox) editor.getComponent(); // System.out.println("Reset GC checkbox at row=" + row + ", col=" + column); checkBox.setSelected(!newState); inspection().announceVMBusyFailure("Watchpoint GC setting"); } break; } default: } } @Override public boolean isCellEditable(int row, int column) { switch (WatchpointsColumnKind.values()[column]) { case READ: return true; case WRITE: return true; case EXEC: return true; case GC: return true; default: return false; } } @Override public Class< ? > getColumnClass(int c) { switch (WatchpointsColumnKind.values()[c]) { case READ: return Boolean.class; case WRITE: return Boolean.class; case EXEC: return Boolean.class; case GC: return Boolean.class; default: return MaxWatchpoint.class; } } MaxWatchpoint rowToWatchpoint(int row) { int count = 0; for (MaxWatchpoint watchpoint : vm().watchpointManager().watchpoints()) { if (count == row) { return watchpoint; } count++; } throw InspectorError.unexpected("WatchpointsInspector.get(" + row + ") failed"); } int findRow(MaxWatchpoint findWatchpoint) { int row = 0; for (MaxWatchpoint watchpoint : vm().watchpointManager().watchpoints()) { if (watchpoint.equals(findWatchpoint)) { return row; } row++; } return -1; } } private final class TagCellRenderer extends JLabel implements TableCellRenderer { TagCellRenderer(Inspection inspection) { super(""); setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { final MaxWatchpoint watchpoint = (MaxWatchpoint) value; JLabel label = this; String labelText = ""; String toolTipText = ""; final InspectorStyle style = preference().style(); setFont(style.defaultFont()); // See if any registers point here final MaxThread thread = focus().thread(); if (thread != null) { final List<MaxRegister> registers = thread.registers().find(watchpoint.memoryRegion()); if (registers.isEmpty()) { label.setForeground(style.memoryDefaultTagTextColor()); } else { final String registerNameList = inspection().nameDisplay().registerNameList(registers); labelText += registerNameList + "-->"; toolTipText += "Register(s): " + registerNameList + " in thread " + inspection().nameDisplay().longName(thread) + " point at this location"; setForeground(style.memoryRegisterTagTextColor()); } } // If a watchpoint is currently triggered here, add a pointer icon. final MaxWatchpointEvent watchpointEvent = vm().state().watchpointEvent(); if (watchpointEvent != null && tableModel.rowToWatchpoint(row).memoryRegion().contains(watchpointEvent.address())) { label.setIcon(style.debugIPTagIcon()); label.setForeground(style.debugIPTagColor()); } else { label.setIcon(null); label.setForeground(null); } label.setText(labelText); label.setToolTipText(toolTipText); label.setBackground(cellBackgroundColor()); return label; } } private final class StartAddressCellRenderer extends DefaultTableCellRenderer implements Prober{ private final Inspection inspection; // WordValueLabels have important user interaction state, so create one per watchpoint and keep them around private final Map<MaxWatchpoint, WeakReference<WordValueLabel> > watchpointToLabelMap = new HashMap<MaxWatchpoint, WeakReference<WordValueLabel> >(); public StartAddressCellRenderer(Inspection inspection) { this.inspection = inspection; setOpaque(true); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = (MaxWatchpoint) value; WeakReference<WordValueLabel> labelReference = watchpointToLabelMap.get(watchpoint); if (labelReference != null && labelReference.get() == null) { // has been collected watchpointToLabelMap.remove(labelReference); labelReference = null; } if (labelReference == null) { labelReference = new WeakReference<WordValueLabel>(new WatchpointStartWordValueLabel(inspection(), ValueMode.WORD, watchpoint)); watchpointToLabelMap.put(watchpoint, labelReference); } final WordValueLabel label = labelReference.get(); label.setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (WeakReference<WordValueLabel> labelReference : watchpointToLabelMap.values()) { final WordValueLabel label = labelReference.get(); if (label != null) { label.redisplay(); } } } public void refresh(boolean force) { for (WeakReference<WordValueLabel> labelReference : watchpointToLabelMap.values()) { final WordValueLabel label = labelReference.get(); if (label != null) { label.refresh(force); } } } private final class WatchpointStartWordValueLabel extends WordValueLabel { private final MaxWatchpoint watchpoint; WatchpointStartWordValueLabel(Inspection inspection, WordValueLabel.ValueMode valueMode, MaxWatchpoint watchpoint) { super(inspection, valueMode, watchpoint.memoryRegion().start(), WatchpointsTable.this); this.watchpoint = watchpoint; setOpaque(true); } @Override public Value fetchValue() { return watchpoint == null ? null : new WordValue(watchpoint.memoryRegion().start()); } } } private final class SizeCellRenderer extends DataLabel.LongAsDecimal implements TableCellRenderer { public SizeCellRenderer(Inspection inspection) { super(inspection, 0); setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = (MaxWatchpoint) value; setValue(watchpoint.memoryRegion().nBytes()); setForeground(cellForegroundColor(row, column)); setBackground(cellBackgroundColor()); return this; } } private final class EndAddressCellRenderer extends DefaultTableCellRenderer implements Prober{ private final Inspection inspection; // WordValueLabels have important user interaction state, so create one per watchpoint and keep them around private final Map<MaxWatchpoint, WeakReference<WordValueLabel> > watchpointToLabelMap = new HashMap<MaxWatchpoint, WeakReference<WordValueLabel> >(); public EndAddressCellRenderer(Inspection inspection) { this.inspection = inspection; } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = (MaxWatchpoint) value; WeakReference<WordValueLabel> labelReference = watchpointToLabelMap.get(watchpoint); if (labelReference != null && labelReference.get() == null) { // has been collected watchpointToLabelMap.remove(labelReference); labelReference = null; } if (labelReference == null) { final WatchpointEndWordValueLabel newLabel = new WatchpointEndWordValueLabel(inspection(), ValueMode.WORD, watchpoint); newLabel.setOpaque(true); labelReference = new WeakReference<WordValueLabel>(newLabel); watchpointToLabelMap.put(watchpoint, labelReference); } final WordValueLabel label = labelReference.get(); setBackground(cellBackgroundColor()); return label; } public void redisplay() { for (WeakReference<WordValueLabel> labelReference : watchpointToLabelMap.values()) { final WordValueLabel label = labelReference.get(); if (label != null) { label.redisplay(); } } } public void refresh(boolean force) { for (WeakReference<WordValueLabel> labelReference : watchpointToLabelMap.values()) { final WordValueLabel label = labelReference.get(); if (label != null) { label.refresh(force); } } } private final class WatchpointEndWordValueLabel extends WordValueLabel { private final MaxWatchpoint watchpoint; WatchpointEndWordValueLabel(Inspection inspection, WordValueLabel.ValueMode valueMode, MaxWatchpoint watchpoint) { super(inspection, valueMode, watchpoint.memoryRegion().end(), WatchpointsTable.this); this.watchpoint = watchpoint; } @Override public Value fetchValue() { return watchpoint == null ? null : new WordValue(watchpoint.memoryRegion().end()); } } } private final class DescriptionCellRenderer extends PlainLabel implements TableCellRenderer { public DescriptionCellRenderer(Inspection inspection) { super(inspection, ""); setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = (MaxWatchpoint) value; final MaxObject object = watchpoint.getWatchedObject(); final StringBuilder sb = new StringBuilder(); sb.append(watchpoint.description()); if (object != null) { sb.append(": ").append(inspection().nameDisplay().referenceLabelText(object)); } final String description = sb.toString(); setText(description); setToolTipText(description); // TODO (mlvdv) Abstract this string, or come up with a method/predicate instead if (watchpoint.memoryRegion().regionName().equals("RegionWatchpoint - GC removed corresponding Object")) { setForeground(Color.RED); } else { setForeground(cellForegroundColor(row, column)); } setBackground(cellBackgroundColor()); return this; } } private final class RegionRenderer extends MemoryRegionValueLabel implements TableCellRenderer { public RegionRenderer(Inspection inspection) { super(inspection, "Start address"); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = (MaxWatchpoint) value; setValue(new WordValue(watchpoint.memoryRegion().start())); setBackground(cellBackgroundColor()); return this; } } private final class TriggerThreadCellRenderer extends PlainLabel implements TableCellRenderer { TriggerThreadCellRenderer(Inspection inspection) { super(inspection, null); setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = tableModel.rowToWatchpoint(row); final MaxWatchpointEvent watchpointEvent = vm().state().watchpointEvent(); if (watchpointEvent != null && watchpointEvent.watchpoint() == watchpoint) { final MaxThread maxThread = watchpointEvent.thread(); setText(inspection().nameDisplay().longName(maxThread)); setToolTipText("Thread \"" + inspection().nameDisplay().longName(maxThread) + "\" stopped at this watchpoint"); } else { setText(""); setToolTipText("No Thread stopped at this watchpoint"); } setForeground(cellForegroundColor(row, column)); setBackground(cellBackgroundColor()); return this; } } private final class TriggerAddressCellRenderer extends PlainLabel implements TableCellRenderer { TriggerAddressCellRenderer(Inspection inspection) { super(inspection, null); setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = tableModel.rowToWatchpoint(row); final MaxWatchpointEvent watchpointEvent = vm().state().watchpointEvent(); if (watchpointEvent != null && watchpointEvent.watchpoint() == watchpoint) { final String addressText = watchpointEvent.address().toHexString(); setText(addressText); setToolTipText("Access of memory location " + addressText + " triggered watchpoint"); } else { setText(""); setToolTipText("No Thread stopped at this watchpoint"); } setForeground(cellForegroundColor(row, column)); setBackground(cellBackgroundColor()); return this; } } private final class TriggerCodeCellRenderer extends PlainLabel implements TableCellRenderer { TriggerCodeCellRenderer(Inspection inspection) { super(inspection, null); setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final MaxWatchpoint watchpoint = tableModel.rowToWatchpoint(row); final MaxWatchpointEvent watchpointEvent = vm().state().watchpointEvent(); if (watchpointEvent != null && watchpointEvent.watchpoint() == watchpoint) { final int watchpointCode = watchpointEvent.eventCode(); String codeName; switch(watchpointCode) { case 1: codeName = "read"; break; case 2: codeName = "write"; break; case 3: codeName = "exec"; break; default: codeName = "unknown"; } codeName += "(" + String.valueOf(watchpointCode) + ")"; setText(codeName); setToolTipText("Watchpoint trigger code=" + codeName); } else { setText(""); setToolTipText("No Thread stopped at this watchpoint"); } setForeground(cellForegroundColor(row, column)); setBackground(cellBackgroundColor()); return this; } } }