crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/ui/editors/CallStackPage.java
author Jussi Ryoma <ext-jussi.s.ryoma@nokia.com>
Tue, 20 Apr 2010 14:41:43 +0300
changeset 4 615035072f7e
permissions -rw-r--r--
Crash Analyser Carbide extension v1.3

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.s60tools.crashanalyser.ui.editors;

import java.util.List;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.ui.PlatformUI;

import com.nokia.s60tools.crashanalyser.files.CrashFile;
import com.nokia.s60tools.crashanalyser.files.SummaryFile;
import com.nokia.s60tools.crashanalyser.resources.HelpContextIDs;
import com.nokia.s60tools.crashanalyser.ui.viewers.CallStackTableViewer;
import com.nokia.s60tools.crashanalyser.ui.views.MainView;
import com.nokia.s60tools.crashanalyser.containers.Stack;
import com.nokia.s60tools.crashanalyser.containers.StackEntry;
import com.nokia.s60tools.crashanalyser.containers.Thread;

public class CallStackPage implements SelectionListener {

	// call stack group UI items
	private Combo comboCallStack;
	private Label labelStackWarning;
	private Button buttonDecodeFile;
	private Table tableCallStack;
	private CallStackTableViewer tableViewerCallStack;
	private Button buttonAllStackEntries;
	private Button buttonSymbolStackEntries;

	private SummaryFile crashFile = null;
	private FontRegistry fontRegistry;
	private Thread selectedThread = null;

	/**
	 * Creates the page
	 * @param parent composite
	 * @param file summary file
	 * @return composite
	 */
	public Composite createPage(Composite parent, SummaryFile file) {
		crashFile = file;
		return doCreate(parent);
	}
	
	/**
	 * Creates the page
	 * @param parent composite
	 * @return composite
	 */
	public Composite createPage(Composite parent) {
		return doCreate(parent);
	}
	
	public void update() {
	//	AutoSizeCallStackTableCells();
	}
	
	/**
	 * Loads data from given file into UI elements.
	 * @param file crash file
	 */
	public void setFile(CrashFile file) {
		if (file != null) {
			crashFile = file;
			initialCallStackTableLoad();
		}
	}
	
	/**
	 * Creates all UI elements to the page
	 * @param parent
	 * @return composite
	 */
	Composite doCreate(Composite parent) {
		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		parent.setLayout(layout);
		parent.setLayoutData(new GridData(GridData.FILL_BOTH));
		fontRegistry = new FontRegistry(Display.getCurrent());
		fontRegistry.put("monospace", new FontData[]{new FontData("Courier", 8, SWT.NORMAL)});
		SashForm sashFormMain = new SashForm(parent, SWT.VERTICAL);
		sashFormMain.setLayoutData(new GridData(GridData.FILL_BOTH));
		createCallStackGroup(sashFormMain);
		
		setHelps();
		
		return parent;
	}

	/**
	 * Creates call stack group
	 * @param parent
	 */
	
	void createCallStackGroup(Composite parent) {
		Group groupCallStack = new Group(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		layout.numColumns = 3;
		groupCallStack.setLayout(layout);
		GridData groupGD = new GridData(GridData.FILL_BOTH);
		groupGD.horizontalSpan = 3;
		groupCallStack.setText("Call Stack");
		groupCallStack.setLayoutData(groupGD);
		
		comboCallStack = new Combo(groupCallStack, SWT.BORDER | SWT.DROP_DOWN | SWT.READ_ONLY);
		comboCallStack.addSelectionListener(this);
		
		labelStackWarning = new Label(groupCallStack, SWT.NONE);
		labelStackWarning.setText("(selected stack is build with heuristic algorithm)");
		labelStackWarning.setVisible(false);
		
		buttonDecodeFile = new Button(groupCallStack, SWT.PUSH);
		buttonDecodeFile.setText("Decode");
		buttonDecodeFile.setVisible(false);
		buttonDecodeFile.addSelectionListener(this);

		tableCallStack = new Table(groupCallStack, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION | 
													SWT.V_SCROLL | SWT.H_SCROLL);
		tableCallStack.setHeaderVisible(true);
		tableCallStack.setFont(fontRegistry.get("monospace"));
		
		GridData tableGD = new GridData(GridData.FILL_BOTH);
		tableGD.horizontalSpan = 3;
		tableCallStack.setLayoutData(tableGD);

		GridData buttonGD = new GridData(GridData.FILL_HORIZONTAL);
		buttonGD.horizontalSpan = 3;

		buttonAllStackEntries = new Button(groupCallStack, SWT.RADIO);
		buttonAllStackEntries.setText("Show all stack entries");
		buttonAllStackEntries.addSelectionListener(this);
		buttonAllStackEntries.setLayoutData(buttonGD);
		
		buttonSymbolStackEntries = new Button(groupCallStack, SWT.RADIO);
		buttonSymbolStackEntries.setText("Show stack entries which have associated symbols");
		buttonSymbolStackEntries.addSelectionListener(this);
		buttonSymbolStackEntries.setLayoutData(buttonGD);
		buttonSymbolStackEntries.setSelection(true);
		
		tableViewerCallStack = new CallStackTableViewer(tableCallStack);

		initialCallStackTableLoad();
		AutoSizeCallStackTableCells();
	}
	
	/**
	 * Packs columns in call stack table nicely
	 */
	void AutoSizeCallStackTableCells() {
		int tableWidth = tableCallStack.getBounds().width;
		tableCallStack.getColumn(CallStackTableViewer.COLUMN_ADDRESS).setWidth(80);
		tableCallStack.getColumn(CallStackTableViewer.COLUMN_VALUE).setWidth(80);
		tableCallStack.getColumn(CallStackTableViewer.COLUMN_OFFSET).setWidth(65);
		tableCallStack.getColumn(CallStackTableViewer.COLUMN_TEXT).setWidth(60);
		int space = tableWidth - 80 - 80 - 65 - 60 - 25;
		int object = space / 3;
		if (object < 70)
			object = 70;
		tableCallStack.getColumn(CallStackTableViewer.COLUMN_OBJECT).setWidth(object);
		tableCallStack.getColumn(CallStackTableViewer.COLUMN_SYMBOL).setWidth(object*2);
	}

	/**
	 * Loads stacks to combo, selects the correct stack as
	 * default set and then loads stack's data to table.
	 */
	void initialCallStackTableLoad() {
		if (crashFile == null)
			return;

	//	int defaultSelectionIndex = 0;

		// show the data of crashed process
		com.nokia.s60tools.crashanalyser.containers.Process process = null;
		
		if (crashFile.getThread() != null) {
			process = crashFile.getProcessByThread(crashFile.getThread().getId());
		} else {
			process = crashFile.getCrashedProcess();
		}
		
		if (process != null) {
			// current UI support only one thread, so show the first thread of first process
			
			if (crashFile.getThread() != null) {
				// Show only thread information (no crash info)
				selectedThread = crashFile.getThread();
			} else {
				selectedThread = crashFile.getCrashedThread();
			}
/*
			if (selectedThread != null) {
				List<RegisterSet> registerSets = selectedThread.getRegisters();
				if (registerSets != null && !registerSets.isEmpty()) {
					// load all register sets to combo
					for (int i = 0; i < registerSets.size(); i++) {
						RegisterSet registerSet = registerSets.get(i);
						// default register to show is the one that contains CPSR register
						if (registerSet.containsCPSR()) 
							defaultSelectionIndex = i;
						comboRegisters.add(registerSet.getName());
						comboRegisters.setData(registerSet.getName(), registerSet);
					}
				}

			}
*/
		}

		
		int selectedIndex = 0;
		if (selectedThread != null) {
			List<Stack> stacks = selectedThread.getStacks();
			if (stacks != null && !stacks.isEmpty()) {
				// add all stacks into combo
				for (int i = 0; i < stacks.size(); i++) {
					Stack stack = stacks.get(i);
					// show stack which contains CPSR register as default
					if (stack.stackRegisterContainsCpsr()) 
						selectedIndex  = i;
					comboCallStack.add(stack.getStackType());
					comboCallStack.setData(stack.getStackType(), stack);
				}
			}
		}
		
		List<Stack> standAloneStacks = crashFile.getStandAloneStacks();
		if (standAloneStacks != null && !standAloneStacks.isEmpty()) {
			for (int i = 0; i < standAloneStacks.size(); i++) {
				Stack stack = standAloneStacks.get(i);
				if (stack.stackRegisterContainsCpsr())
					selectedIndex = comboCallStack.getItemCount() + i;
				comboCallStack.add(stack.getStackType());
				comboCallStack.setData(stack.getStackType(), stack);
			}
		}

		if (comboCallStack.getItemCount() > 0) {
			comboCallStack.select(selectedIndex);
			loadCallStackTable(true);
		}
	}
	
	/**
	 * Loads call stack table according to which stack is
	 * selected in combo. 
	 */
	void loadCallStackTable(boolean autoSizeCells) {
		tableCallStack.removeAll();
		labelStackWarning.setVisible(false);
		
		if (comboCallStack.getItemCount() < 1)
			return;
		
		try {
			Stack stack = (Stack)comboCallStack.getData(comboCallStack.getText());
			boolean containsAccurate = stack.containsAccurateStackEntries();
			labelStackWarning.setVisible(!containsAccurate);
			List<StackEntry> stackEntries = stack.getStackEntries();
			if (stackEntries != null && !stackEntries.isEmpty()) {
				for (int i = 0; i < stackEntries.size(); i++) {
					StackEntry stackEntry = stackEntries.get(i);
					newStackTableItem(stackEntry, containsAccurate);
				}
				if (autoSizeCells)
					AutoSizeCallStackTableCells();
			}
			
			// nothing in stack
			if (stackEntries == null || stackEntries.isEmpty()) {
				disableStack();
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Disables stack UI elements
	 */
	void disableStack() {
		labelStackWarning.setVisible(true);
		labelStackWarning.setText("Symbols were not available while creating stack.");
		buttonDecodeFile.setVisible(true);
		comboCallStack.setEnabled(false);
		buttonAllStackEntries.setEnabled(false);
		buttonSymbolStackEntries.setEnabled(false);
		tableCallStack.setEnabled(false);
	}
	


	public void widgetDefaultSelected(SelectionEvent arg0) {
		// no implementation required
	}

	public void widgetSelected(SelectionEvent event) {
		// stack was changed in combo
		if (event.widget == comboCallStack) {
			loadCallStackTable(true);
		// stack radio button changes
		} else if (event.widget == buttonAllStackEntries || 
					event.widget == buttonSymbolStackEntries) {
			loadCallStackTable(false);
		// decode button was pressed
		} else if (event.widget == buttonDecodeFile) {
			MainView mv = MainView.showAndReturnYourself(true);
			mv.decodeFile(crashFile);
		}
	}

	/**
	 * Adds new stack table row.
	 * @param stackEntry row data
	 * @param stackContainsAccurateEntries affects row colorings
	 */
	void newStackTableItem(StackEntry stackEntry, boolean stackContainsAccurateEntries) {
		// if 'Show stack entries which have associated symbols' radio button is selected and
		// if stack contains accurate stack entries, and this stack entry is not accurate
		// don't add it to table (unless this stack entry is current stack pointer or register based)
		if (buttonSymbolStackEntries.getSelection() &&
			stackContainsAccurateEntries && 
			!stackEntry.accurate() && 
			!stackEntry.currentStackPointer() &&
			!stackEntry.registerBased()) {
			return;
		// if 'Show stack entries which have associated symbols' radio button is selected
		// if stack doesn't contain accurate stack entries, don't show current stack entry
		// if it doesn't have associated symbol (unless this stack entry is current stack pointer or register based)
		} else if (buttonSymbolStackEntries.getSelection() &&
					!stackContainsAccurateEntries &&
					"".equals(stackEntry.getSymbol()) &&
					!stackEntry.currentStackPointer() &&
					!stackEntry.registerBased())
			return;

		TableItem item = new TableItem(tableCallStack, SWT.NONE);
		item.setText(new String[] {stackEntry.getAddress(),
									stackEntry.getSymbol(),
									stackEntry.getValue(),
									stackEntry.getOffset(),
									stackEntry.getObject(),
									stackEntry.getText()});
		
		item.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
		item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
		
		// item is current stack pointer FG black, BG light pink
		if (stackEntry.currentStackPointer()) {
			item.setBackground(new Color(Display.getCurrent(), 255, 182, 193)); // light pink
		// PC or LR
		} else if (stackEntry.registerBased()) {
			item.setBackground(new Color(Display.getCurrent(), 173, 216, 230)); // light blue
		// item is out of stack bound FG gray, BG white
		} else if (stackEntry.outsideStackBounds()) {
			item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
		// item has no symbols, FG black, BG white
		} else if ("".equals(stackEntry.getSymbol())) {
		} else
			// if stack contains accurate entries, show "ghost" entries as gray
			if (stackContainsAccurateEntries) {
				// color accurate stack entries black or blue
				if (stackEntry.accurate()) {
					// ram loaded code (instead of execute-in-place code) FG blue
					if (!stackEntry.xip()) {
						item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE));
					}
				// not accurate stack entry FG gray
				} else {
					item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
				}
			// stack isn't accurate,
			} else {
				// ram loaded code (instead of execute-in-place code)
				if (!stackEntry.xip()) {
					item.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE));
			}
		}
		
		item.setData(stackEntry);
	}

	/**
	 * Sets context sensitive help ids to UI elements
	 */
	void setHelps() {

		PlatformUI.getWorkbench().getHelpSystem().setHelp(tableCallStack,
				HelpContextIDs.CRASH_ANALYSER_HELP_CRASH_VISUALISER);	
		PlatformUI.getWorkbench().getHelpSystem().setHelp(comboCallStack,
				HelpContextIDs.CRASH_ANALYSER_HELP_CRASH_VISUALISER);	
		PlatformUI.getWorkbench().getHelpSystem().setHelp(buttonAllStackEntries,
				HelpContextIDs.CRASH_ANALYSER_HELP_CRASH_VISUALISER);	
		PlatformUI.getWorkbench().getHelpSystem().setHelp(buttonDecodeFile,
				HelpContextIDs.CRASH_ANALYSER_HELP_CRASH_VISUALISER);	
		PlatformUI.getWorkbench().getHelpSystem().setHelp(buttonSymbolStackEntries,
				HelpContextIDs.CRASH_ANALYSER_HELP_CRASH_VISUALISER);	
	}

}