/*
* Copyright (c) 2008, 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 static com.sun.max.tele.MaxProcessState.*;
import java.awt.*;
import java.awt.print.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import com.sun.max.ins.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.gui.TableColumnVisibilityPreferences.TableColumnViewPreferenceListener;
import com.sun.max.ins.view.*;
import com.sun.max.ins.view.InspectionViews.ViewKind;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.runtime.*;
/**
* A singleton views that displays thread local areas for the thread the VM that is the current user focus.
*/
public final class ThreadLocalsView extends AbstractView<ThreadLocalsView> implements TableColumnViewPreferenceListener {
private static final int TRACE_VALUE = 1;
private static final ViewKind VIEW_KIND = ViewKind.THREAD_LOCALS;
private static final String SHORT_NAME = "Thread Locals";
private static final String LONG_NAME = "Thread Locals View";
private static final String GEOMETRY_SETTINGS_KEY = "threadlocalsViewGeometry";
private static final SafepointPoll.State DEFAULT_STATE_SELECTION = SafepointPoll.State.ENABLED;
private static final Map<MaxThread, SafepointPoll.State> stateSelections = new HashMap<MaxThread, SafepointPoll.State>();
public static final class ThreadLocalsViewManager extends AbstractSingletonViewManager<ThreadLocalsView> {
protected ThreadLocalsViewManager(Inspection inspection) {
super(inspection, VIEW_KIND, SHORT_NAME, LONG_NAME);
}
@Override
protected ThreadLocalsView createView(Inspection inspection) {
return new ThreadLocalsView(inspection);
}
}
// Will be non-null before any instances created.
private static ThreadLocalsViewManager viewManager = null;
public static ThreadLocalsViewManager makeViewManager(Inspection inspection) {
if (viewManager == null) {
viewManager = new ThreadLocalsViewManager(inspection);
}
return viewManager;
}
// This is a singleton viewer, so only use a single level of view preferences.
private final ThreadLocalsViewPreferences viewPreferences;
private MaxThread thread;
private InspectorTabbedPane tabbedPane;
private ThreadLocalsView(Inspection inspection) {
super(inspection, VIEW_KIND, GEOMETRY_SETTINGS_KEY);
Trace.begin(TRACE_VALUE, tracePrefix() + " initializing");
viewPreferences = ThreadLocalsViewPreferences.globalPreferences(inspection());
viewPreferences.addListener(this);
createFrame(true);
forceRefresh();
Trace.end(TRACE_VALUE, tracePrefix() + " initializing");
}
@Override
public String getTextForTitle() {
String title = viewManager.shortName() + ": ";
if (!inspection().hasProcess()) {
title += inspection().nameDisplay().noProcessShortText();
} else if (thread != null) {
title += inspection().nameDisplay().longNameWithState(thread);
} else {
title += inspection().nameDisplay().unavailableDataShortText();
}
return title;
}
@Override
protected void createViewContent() {
thread = focus().thread();
SafepointPoll.State initialStateSelection = stateSelections.get(thread);
if (initialStateSelection == null) {
initialStateSelection = DEFAULT_STATE_SELECTION;
}
ThreadLocalsAreaPanel initialPanelSelection = null;
tabbedPane = new InspectorTabbedPane(inspection());
if (thread != null) {
for (SafepointPoll.State state : SafepointPoll.State.CONSTANTS) {
final MaxThreadLocalsArea tla = thread.localsBlock().tlaFor(state);
if (tla != null) {
final ThreadLocalsAreaPanel panel = new ThreadLocalsAreaPanel(inspection(), this, thread, tla, viewPreferences);
if (state == initialStateSelection) {
initialPanelSelection = panel;
}
tabbedPane.add(state.toString(), panel);
}
}
if (initialPanelSelection != null) {
tabbedPane.setSelectedComponent(initialPanelSelection);
}
tabbedPane.addChangeListener(new ChangeListener() {
// Refresh a newly exposed pane to be sure it is current
public void stateChanged(ChangeEvent event) {
// TODO (mlvdv) Data reading PATCH, there should be a more systematic way of handling this.
if (vm().state().processState() == MaxProcessState.TERMINATED) {
return;
}
final ThreadLocalsAreaPanel tlaPanel = (ThreadLocalsAreaPanel) tabbedPane.getSelectedComponent();
if (tlaPanel != null) {
tlaPanel.refresh(true);
stateSelections.put(thread, tlaPanel.getSafepointState());
}
}
});
}
setContentPane(tabbedPane);
setTitle();
// Populate menu bar
makeMenu(MenuKind.DEFAULT_MENU).add(defaultMenuItems(MenuKind.DEFAULT_MENU));
final InspectorMenu memoryMenu = makeMenu(MenuKind.MEMORY_MENU);
memoryMenu.add(actions().viewSelectedThreadLocalsBlockMemory("View memory for thread's locals block"));
for (SafepointPoll.State state : SafepointPoll.State.CONSTANTS) {
memoryMenu.add(actions().viewSelectedThreadLocalsAreaMemory(state, "View memory for thread's " + state.name() + " area"));
}
memoryMenu.add(actions().viewSelectedThreadStackMemory("View memory for thread's stack"));
memoryMenu.add(defaultMenuItems(MenuKind.MEMORY_MENU));
memoryMenu.add(views().activateSingletonViewAction(ViewKind.ALLOCATIONS));
makeMenu(MenuKind.VIEW_MENU).add(defaultMenuItems(MenuKind.VIEW_MENU));
final InspectorMenu editMenu = makeMenu(MenuKind.EDIT_MENU);
Watchpoints.buildThreadLocalWatchpointMenu(inspection(), editMenu);
}
@Override
protected void refreshState(boolean force) {
if (inspection().hasProcess()) {
boolean panelsAddedOrRemoved = false;
for (SafepointPoll.State state : SafepointPoll.State.CONSTANTS) {
ThreadLocalsAreaPanel panel = null;
for (Component component : tabbedPane.getComponents()) {
final ThreadLocalsAreaPanel tlaPanel = (ThreadLocalsAreaPanel) component;
if (tlaPanel.getSafepointState() == state) {
panel = tlaPanel;
}
}
final MaxThreadLocalsArea tla = thread.localsBlock().tlaFor(state);
if (tla != null) {
if (panel == null) {
tabbedPane.add(state.toString(), new ThreadLocalsAreaPanel(inspection(), this, thread, tla, viewPreferences));
panelsAddedOrRemoved = true;
}
} else {
if (panel != null) {
tabbedPane.remove(panel);
panelsAddedOrRemoved = true;
}
}
}
if (panelsAddedOrRemoved) {
reconstructView();
}
// Only need to refresh the panel that's visible, as long as we refresh them when they become visible
final ThreadLocalsAreaPanel tlaPanel = (ThreadLocalsAreaPanel) tabbedPane.getSelectedComponent();
if (tlaPanel != null) {
tlaPanel.refresh(force);
}
}
// The title displays thread state, so must be updated.
setTitle();
}
@Override
public void threadFocusSet(MaxThread oldThread, MaxThread thread) {
reconstructView();
}
@Override
public void addressFocusChanged(Address oldAddress, Address newAddress) {
forceRefresh();
}
@Override
public void watchpointSetChanged() {
if (vm().state().processState() != TERMINATED) {
forceRefresh();
}
}
@Override
public InspectorAction getViewOptionsAction() {
return new InspectorAction(inspection(), "View Options") {
@Override
public void procedure() {
new TableColumnVisibilityPreferences.ColumnPreferencesDialog<ThreadLocalVariablesColumnKind>(inspection(), viewManager.shortName() + " View Options", viewPreferences);
}
};
}
@Override
public InspectorAction getPrintAction() {
return new InspectorAction(inspection(), "Print") {
@Override
public void procedure() {
final ThreadLocalsAreaPanel tlaPanel = (ThreadLocalsAreaPanel) tabbedPane.getSelectedComponent();
final String name = getTextForTitle() + " " + tlaPanel.getSafepointState().toString();
final MessageFormat footer = new MessageFormat(vm().entityName() + ": " + name + " Printed: " + new Date() + " -- Page: {0, number, integer}");
try {
final InspectorTable inspectorTable = tlaPanel.getTable();
assert inspectorTable != null;
inspectorTable.print(JTable.PrintMode.FIT_WIDTH, null, footer);
} catch (PrinterException printerException) {
gui().errorMessage("Print failed: " + printerException.getMessage());
}
}
};
}
public void tableColumnViewPreferencesChanged() {
reconstructView();
}
@Override
public void viewClosing() {
viewPreferences.removeListener(this);
super.viewClosing();
}
@Override
public void vmProcessTerminated() {
reconstructView();
}
}