--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/ui/viewers/CallStackTableViewer.java Thu Feb 11 15:06:45 2010 +0200
@@ -0,0 +1,537 @@
+/*
+* Copyright (c) 2008 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.viewers;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.ui.*;
+import org.eclipse.ui.editors.text.*;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.menus.*;
+import sun.awt.windows.*;
+import com.nokia.s60tools.crashanalyser.model.*;
+import com.nokia.s60tools.crashanalyser.resources.*;
+import com.nokia.s60tools.crashanalyser.containers.*;
+import com.nokia.s60tools.crashanalyser.ui.console.CrashAnalyserEditorConsole;
+import com.nokia.s60tools.util.sourcecode.*;
+import java.util.*;
+import java.net.URI;
+import java.io.*;
+import java.awt.datatransfer.*;
+import java.awt.Toolkit;
+
+/**
+ * Table viewer for call stack table. Call stack table is used in
+ * SummaryPage.java (Crash Data page in Crash Visualiser editor)
+ *
+ */
+public class CallStackTableViewer extends CrashAnalyserTableViewer {
+ Action actionCopySelectionToClipboardAsRichText;
+ Action actionCopySelectionToClipboardAsPlainText;
+ Action actionCopyStackToClipboardAsRichText;
+ Action actionCopyStackToClipboardAsPlainText;
+ Action actionOpenSourceFile;
+ Action actionDoubleClick;
+ Table tableControl;
+
+ public final static int COLUMN_ADDRESS = 0;
+ public final static int COLUMN_SYMBOL = 1;
+ public final static int COLUMN_VALUE = 2;
+ public final static int COLUMN_OFFSET = 3;
+ public final static int COLUMN_OBJECT = 4;
+ public final static int COLUMN_TEXT = 5;
+
+ MenuManager subMenuSdk;
+
+ /**
+ * Constructor
+ * @param table table where this viewer is used
+ */
+ public CallStackTableViewer(Table table) {
+ super(table);
+
+ tableControl = table;
+
+ doCreateActions();
+ doCreateContextMenu();
+
+ TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
+ column.setLabelProvider(labelProvider);
+ column.getColumn().setText("");
+
+ column = new TableViewerColumn(this, SWT.NONE);
+ column.setLabelProvider(labelProvider);
+ column.getColumn().setText("Symbol");
+
+ column = new TableViewerColumn(this, SWT.NONE);
+ column.setLabelProvider(labelProvider);
+ column.getColumn().setText("Address");
+
+ column = new TableViewerColumn(this, SWT.NONE);
+ column.setLabelProvider(labelProvider);
+ column.getColumn().setText("Offset");
+
+ column = new TableViewerColumn(this, SWT.NONE);
+ column.setLabelProvider(labelProvider);
+ column.getColumn().setText("Object");
+
+ column = new TableViewerColumn(this, SWT.NONE);
+ column.setLabelProvider(labelProvider);
+ column.getColumn().setText("Data");
+
+ addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ actionDoubleClick.run();
+ }
+ });
+ }
+
+ /**
+ * Creates pop-up menu actions
+ */
+ private void doCreateActions() {
+ actionCopySelectionToClipboardAsRichText = new Action("as Rich Text") {
+ public void run() {
+ copyToClipboard(tableControl, true, true);
+ }
+ };
+
+ actionCopyStackToClipboardAsRichText = new Action("as Rich Text") {
+ public void run() {
+ copyToClipboard(tableControl, false, true);
+ }
+ };
+
+ actionCopySelectionToClipboardAsPlainText = new Action("as Plain Text") {
+ public void run() {
+ copyToClipboard(tableControl, true, false);
+ }
+ };
+
+ actionCopyStackToClipboardAsPlainText = new Action("as Plain Text") {
+ public void run() {
+ copyToClipboard(tableControl, false, false);
+ }
+ };
+
+ actionOpenSourceFile = new Action("Open Source File") {
+ public void run() {
+ openSourceFile(tableControl);
+ }
+ };
+
+ actionDoubleClick = new Action() {
+ public void run() {
+ openSourceFile(tableControl);
+ }
+ };
+ }
+
+ /**
+ * Setting the focus in opened file there where the method name occurs,
+ * must call after file is opened and only if opening was successful
+ * @param location
+ * @throws CoreException
+ */
+ private static void setFocusToLineWhereMethodIs(
+ final SourceFileLocation location) throws CoreException {
+ //Runnable to open new file
+ final IWorkspaceRunnable runSetFocus = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ // do the actual work in here
+
+ try {
+ //Setting focus to correct line
+ IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IEditorPart activeEditor = activePage.getActiveEditor();
+
+ if(activeEditor != null && activeEditor instanceof TextEditor){
+
+ // This is actually an instance of 'org.eclipse.cdt.internal.ui.editor.CEditor'
+ // that extends org.eclipse.ui.editors.text.TextEditor
+ TextEditor editor = (TextEditor) activeEditor;
+ IDocument doc = getDocument(editor);
+ if(doc != null){
+
+ String text = doc.get();
+ int methodOffset = location.getMethodOffset();
+ if(methodOffset == SourceFileLocation.OFFSET_NOT_FOUND){
+ // Removing parameters and getting new offset.
+ String methodNameWithOutParams = location.getMethodName();
+ methodNameWithOutParams = methodNameWithOutParams.substring(0, methodNameWithOutParams.indexOf("(")); //$NON-NLS-1$
+ methodOffset = text.indexOf(methodNameWithOutParams);
+
+ if(methodOffset == SourceFileLocation.OFFSET_NOT_FOUND){
+ // Removing possible namespace and getting new offset.
+ String separator = "::"; //$NON-NLS-1$
+ int separatorLocation = methodNameWithOutParams.lastIndexOf(separator);
+ if(separatorLocation > 0){
+ methodNameWithOutParams = methodNameWithOutParams.substring(separatorLocation + separator.length());
+ methodOffset = text.indexOf(methodNameWithOutParams);
+ }
+ }
+ }
+
+ editor.setHighlightRange(methodOffset, 0, true);
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Status status = new Status(IStatus.ERROR,
+ "com.nokia.s60tools.crashanalyser", 0, e //$NON-NLS-1$
+ .getMessage(), e);
+
+ throw new CoreException(status);
+ }
+
+ }
+ };
+
+
+ ResourcesPlugin.getWorkspace().run(runSetFocus, null, IWorkspace.AVOID_UPDATE, null);
+ }
+
+ /**
+ * Returns the document interface for the currently active document
+ * in the given editor.
+ * @param editor Editor to ask currently active document from.
+ * @return Document interface if found, otherwise <code>null</code>.
+ */
+ private static IDocument getDocument(TextEditor editor) {
+
+ TextFileDocumentProvider documentProvider = (TextFileDocumentProvider) editor.getDocumentProvider();
+ if(documentProvider != null){
+ return documentProvider.getDocument(editor.getEditorInput());
+ }
+ return null;
+ }
+
+ /**
+ * Shows a message box with given message
+ * @param message
+ */
+ private static void showMessage(String message, Shell shell) {
+ MessageDialog.openInformation(
+ shell,
+ "Crash Analyser",
+ message);
+ }
+
+ /**
+ * Tries to open source file for the selected line in the call stack table
+ * @param tableControl
+ */
+ static void openSourceFile(Table tableControl) {
+ try {
+ if (tableControl.getSelection() != null &&
+ tableControl.getSelection().length == 1) {
+ StackEntry stackEntry =
+ (StackEntry)tableControl.getSelection()[0].getData();
+
+ // we need code segment in order to be able to open source code,
+ // if we don't find code segment, notify user
+ if (stackEntry == null ||
+ "".equals(stackEntry.getCodeSegmentName())) {
+ showMessage("CODE SEGMENT NOT FOUND\n\nThe selected row does not contain information from which Code Segment (e.g. dll, exe) its address can be found. Source file cannot be opened.", tableControl.getShell());
+ return;
+ }
+
+ String currentSdkName = SourceSdkManager.getCurrentSkdName();
+ // user must first choose an active SDK. If it has not been chosen, notify user
+ if (currentSdkName == null || "".equals(currentSdkName)) {
+ showMessage("ACTIVE SDK NOT SELECTED.\n\n Please select Active SDK first by right-clicking in the Call Stack table.", tableControl.getShell());
+ return;
+ }
+
+ String epocroot = SourceSdkManager.getEpocroot(currentSdkName);
+ // user might have e.g. selected long ago an sdk which is not available anymore
+ if (epocroot == null) {
+ showMessage("INVALID ACTIVE SDK.\n\n Please select a valid Active SDK by right-clicking in the Call Stack table.", tableControl.getShell());
+ return;
+ }
+ File epoc = new File(epocroot);
+
+ // epocroot for the selected SDK if not valid, notify user
+ if (!epoc.isDirectory() || !epoc.exists()) {
+ String message = "INVALID EPOCROOT.\n\nThe EPOCROOT (" +
+ epocroot +
+ ") of selected Active SDK ("+
+ currentSdkName +
+ ") is not valid. Please check your SDK settings from Window > Preferences > Carbide.c++ > SDK Preferences, or select a different Active SDK by right-clicking in the Call Stack table.";
+ showMessage(message, tableControl.getShell());
+ return;
+ }
+ // currentSdkName is e.g. 'SDK_Name armv5 urel', we need to get out, build and variant
+ int lastSpace = currentSdkName.lastIndexOf(" ");
+ int secondToLastSpace = currentSdkName.lastIndexOf(" ", lastSpace-1);
+
+ String build = currentSdkName.substring(lastSpace+1, currentSdkName.length()); //e.g. urel
+ String variant = currentSdkName.substring(secondToLastSpace+1, lastSpace); // e.g. armv5
+
+ String methodNameAsItsInMapFile = tableControl.getSelection()[0].getText(COLUMN_SYMBOL);
+ String dllName = stackEntry.getCodeSegmentName();
+
+ ISourceFinder finder = SourceFinderFactory.createMapSourceFinder(CrashAnalyserEditorConsole.getInstance());
+
+ SourceFileLocation location =
+ finder.findSourceFileByMethodName(methodNameAsItsInMapFile, dllName, variant, build, epocroot);
+
+ File file = new File(location.getSourceFileLocation());
+ // we could not find the source file, notify user
+ if(file == null || !file.exists()){
+ showMessage("SOURCE FILE CANNOT BE FOUND\n\n" +
+ file.getName() +
+ " was not found from " +
+ FileOperations.getFolder(location.getSourceFileLocation()) +
+ ". Please make sure you have selected a correct Active SDK by right-clicking in the Call Stack table. It is also possible that you don't have all source files for your Active SDK present. Source file cannot be opened.",
+ tableControl.getShell());
+ return;
+ }
+
+ //Create URI to open file
+ String uriStr = location.getSourceFileLocation().replace("\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ uriStr = "file://" + uriStr; //$NON-NLS-1$
+ final URI srcURI = new URI(uriStr);
+
+ //Find default editor for that file
+ IEditorRegistry reg = PlatformUI.getWorkbench().getEditorRegistry();
+
+ IEditorDescriptor editor = reg.getDefaultEditor(file.getName());
+ //We open editor by it's ID
+ final String editorId = editor.getId();
+
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ try {
+ IEditorPart part = IDE.openEditor(page, srcURI, editorId, true);
+ if(part != null){
+ //Set focus to correct line
+ setFocusToLineWhereMethodIs(location);
+ }
+ } catch (PartInitException e) {
+ e.printStackTrace();
+ }
+ }
+ }catch (CannotFoundFileException ex) {
+ String newLine = System.getProperty("line.separator");
+ showMessage(ex.getMessage() + newLine + newLine + "Please check that you have selected correct Active SDK, by right-clicking in the Call Stack table.", tableControl.getShell());
+ } catch (Exception e) {
+ e.printStackTrace();
+ showMessage("Unable to find source file. Please check your SDK properties.", tableControl.getShell());
+ }
+ }
+
+ /**
+ * Copies data from table into clipboard
+ * @param table table from which data is copied from
+ * @param selection if true, only the selected rows are copied, if false, all rows are copied
+ * @param richText if true, data is copied in rich-text format, if false, data is copied in plain text format
+ */
+ static void copyToClipboard(Table table, boolean selection, boolean richText) {
+ TableItem[] items = null;
+ // copy only selected rows
+ if (selection)
+ items = table.getSelection();
+ // copy all rows
+ else
+ items = table.getItems();
+
+ if (items != null && items.length > 0) {
+ // rich-text format
+ if (richText) {
+ try {
+ String data = HtmlFormatter.formatStackForClipboard(items, table.getColumnCount());
+ byte[] bytes = convertToHTMLFormat(data);
+ PEBClip clip = new PEBClip();
+ clip.setData( WDataTransferer.CF_HTML, bytes);
+ } catch (Exception e) {
+ }
+ // plain text format
+ } else {
+ String separator = System.getProperty("line.separator");
+ String data = "";
+ for (int i = 0; i < items.length; i++) {
+ TableItem item = items[i];
+ data += String.format("%-10s %-10s %-6s %-6s %s %s",
+ item.getText(COLUMN_ADDRESS),
+ item.getText(COLUMN_VALUE),
+ item.getText(COLUMN_TEXT),
+ item.getText(COLUMN_OFFSET),
+ item.getText(COLUMN_OBJECT),
+ item.getText(COLUMN_SYMBOL)) +
+ separator;
+ }
+ StringSelection stringSelection = new StringSelection(data);
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(stringSelection, null);
+ }
+ }
+ }
+
+ /**
+ * surrounds the html with this envelope, ready for
+ * windows clipboard (jdk support can be made better)
+ * <pre>
+ * Version:1.0
+ * StartHTML:00000000000
+ * EndHTML:00000000000
+ * StartFragment:00000000000
+ * EndFragment:00000000000
+ * <!--StartFragment-->
+ * ...
+ * <!-- EndFragment-- >
+ * </pre>
+ * We have to return a byte array 'cause in Windows the html needs to be utf-8
+ * encoded. And because we have to calculate char-offsets, we encode it here.
+ * @param htmlText
+ * @return byte[]
+ */
+ static byte[] convertToHTMLFormat(String htmlText) {
+ try {
+ String sep = "\r\n";
+ String header = "Version:1.0"+ sep +
+ "StartHTML:00000000000"+ sep +
+ "EndHTML:00000000000"+ sep +
+ "StartFragment:00000000000"+ sep +
+ "EndFragment:00000000000" + sep;
+
+ String html = "<!--StartFragment-->\r\n" + htmlText + "<!--EndFragment-->\r\n";
+
+ byte[] bHtml = html.getBytes("UTF-8");// encode first 'cause it may grow
+
+ int headerLen = header.length();
+ int htmlLen = bHtml.length;
+
+ StringBuffer buf = new StringBuffer(header);
+ setValue( buf, "StartHTML", headerLen-1);
+ setValue( buf, "EndHTML", headerLen + htmlLen-1);
+ setValue( buf, "StartFragment", headerLen-1);
+ setValue( buf, "EndFragment", headerLen + htmlLen-1);
+ byte[] bHeader = buf.toString().getBytes("UTF-8");// should stay the same (no nonASCII chars in header)
+
+ byte result[] = new byte[headerLen + htmlLen ];
+ System.arraycopy(bHeader, 0, result, 0, bHeader.length);
+ System.arraycopy(bHtml, 0, result, bHeader.length, bHtml.length);
+
+ return result;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Replaces name+":00000000000" with name+":xxxxxxxxxxx" where xxx... is the '0' padded value.
+ * Value can't be to long, since maxint can be displayed with 11 digits. If value is below zero
+ * there is enough place (10 for the digits 1 for sign).<br>
+ * If the search is not found nothing is done.
+ * @param src
+ * @param name
+ * @param value
+ */
+ private static void setValue( StringBuffer src, String name, int value){
+ int val = value;
+ String search = name+":00000000000";
+ int pos = src.indexOf(search);
+ if (pos ==-1) return;// not found, do nothing
+
+ boolean belowZero = val<0;
+ if (belowZero) val = -val;
+
+ src.replace(pos+search.length()-(val+"").length(), pos+search.length(), val+"");
+ if (belowZero) src.setCharAt(pos+name.length()+1,'-'); // +1 'cause of ':' in "SearchMe:"
+ }
+
+ /**
+ * creates context menu accordingly to what is selected in the table.
+ */
+ private void doCreateContextMenu() {
+
+ // Active SDK menu
+ subMenuSdk = new MenuManager("Active SDK");
+ subMenuSdk.setRemoveAllWhenShown(true);
+ subMenuSdk.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ Map<String, String> sdks = SourceSdkManager.getAllSdks();
+ String currentSdk = SourceSdkManager.getCurrentSkdName();
+ // if there are sdks
+ if (sdks != null && !sdks.isEmpty()) {
+ String[] sdkNames = sdks.keySet().toArray(new String[sdks.size()]);
+ java.util.Arrays.sort(sdkNames);
+ // go through all found sdks and and them to pop-up menu
+ for (int i = 0; i < sdkNames.length; i++) {
+ String sdk = sdkNames[i];
+ CommandContributionItemParameter p =
+ new CommandContributionItemParameter(PlatformUI.getWorkbench().getActiveWorkbenchWindow(),
+ null,
+ "com.nokia.s60tools.crashanalyser.commands.SdkSelection",
+ CommandContributionItem.STYLE_PUSH);
+ p.label = sdk;
+ // if this sdk is selected as Active sdk, draw an circular image to
+ // this menu item to indicate that this is the active sdk
+ if (sdk.equalsIgnoreCase(currentSdk)) {
+ p.icon = ImageResourceManager.getImageDescriptor((ImageKeys.SELECTED_SDK));
+ }
+ CommandContributionItem item = new CommandContributionItem(p);
+ subMenuSdk.add(item);
+ }
+ }
+ }
+ });
+
+ final MenuManager subMenuSelection = new MenuManager("Copy Selection to Clipboard");
+ subMenuSelection.add(actionCopySelectionToClipboardAsPlainText);
+ subMenuSelection.add(actionCopySelectionToClipboardAsRichText);
+
+ final MenuManager subMenuFull = new MenuManager("Copy Whole Stack to Clipboard");
+ subMenuFull.add(actionCopyStackToClipboardAsPlainText);
+ subMenuFull.add(actionCopyStackToClipboardAsRichText);
+
+ MenuManager manager = new MenuManager("#PopupMenu");
+ Menu menu = manager.createContextMenu(getControl());
+ getControl().setMenu(menu);
+
+ manager.setRemoveAllWhenShown(true);
+ manager.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ Table table = (Table)getControl();
+ if (table.getSelection() != null) {
+ // add 'Open source file' and 'Active SDK' menu items to pop-up menu
+ // only if exactly one row is selected in table and if that
+ // row's symbol column is not empty
+ if (table.getSelection().length == 1 &&
+ !"".equals(table.getSelection()[0].getText(COLUMN_SYMBOL))) {
+ manager.add(actionOpenSourceFile);
+ manager.add(subMenuSdk);
+ manager.add(new Separator());
+ }
+ manager.add(subMenuSelection);
+ manager.add(subMenuFull);
+ }
+ }
+ });
+ }
+}
+