Java tutorial
/* * Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved. * * This program and the accompanying materials are made available * under the terms of the Eclipse Public License, Version 1.0, * which accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html * */ package net.rim.ejde.internal.ui.views.profiler; import java.awt.Desktop; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Comparator; import java.util.Enumeration; import net.rim.ejde.internal.core.ContextManager; import net.rim.ejde.internal.core.RimIDEUtil; import net.rim.ejde.internal.ui.preferences.PreferenceConstants; import net.rim.ejde.internal.ui.views.BasicDebugView; import net.rim.ejde.internal.util.DebugUtils; import net.rim.ejde.internal.util.Messages; import net.rim.ejde.internal.util.ProjectUtils; import net.rim.ide.RIA; import net.rim.ide.RIA.ProfileType; import net.rim.ide.core.IDEError; import net.rim.ide.core.ProfileData; import net.rim.ide.core.ProfileData.SourceResolver; import net.rim.ide.core.ProfileItem; import net.rim.ide.core.ProfileItemSource; import net.rim.ide.core.ProfileLine; import net.rim.ide.core.ProfileMethod; import net.rim.ide.core.ProfileSourceLine; import net.rim.ide.core.Util; import net.rim.tools.compiler.debug.DebugMethod; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.ILaunch; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; /** * View to display profile data. */ public class ProfilerView extends BasicDebugView implements SourceResolver { public static final String PROFILER_VIEW_ID = "net.rim.ejde.ui.viewers.ProfilerView"; // Indexes of profile tabs final static public int INDEX_OF_TAB_SUMMARY = 0; final static public int INDEX_OF_TAB_METHOD = 1; final static public int INDEX_OF_TAB_SOURCE = 2; private static final Logger log = Logger.getLogger(ProfilerView.class); private TabFolder _tabFolder; private int _whatToProfile = 0; private boolean _methodTimeType; private ProfileTab[] _profileTabs; private ProfileData _pd; boolean _isInitialized = false; private static final String BLACKBERRY_PROFILEVISDESKTOP_TMP_FILE_PREFIX = "bbprof.prefix."; private static final String BLACKBERRY_PROFILEVISDESKTOP_TMP_FILE_SUFFIX = ".bbprof"; /** * Constructs a ProfilerView instance. * * @throws CoreException * */ public ProfilerView() throws CoreException { super(FORWARD_BUTTON | BACKWARD_BUTTON | REFRESH_BUTTON | CLEAR_BUTTON | OPTIONS_BUTTON | SAVE_BUTTON | SAVE_TO_XML | SAVE_RAW_TO_XML); RIA ria = RIA.getCurrentDebugger(); if (ria != null && !_isInitialized) { initProfileParameters(ria); } } /** * Initializes all parameters related to profile. * * @throws CoreException * */ private void initProfileParameters(RIA ria) { if (ria != null) { if (!DebugUtils.isRIMDebuggerRunning()) { return; } if (!ria.getProfileEnabled()) { ria.setProfileEnabled(true); } cleanMessage(); try { // get profile options from preference store IPreferenceStore ps = ContextManager.PLUGIN.getPreferenceStore(); _whatToProfile = ps.getInt(PreferenceConstants.NET_RIM_EJDE_UI_VIEWS_WHATTOPROFILE); ProfileType[] types; // MKS 2486071 String debugAttachedTo = ria.getDebugAttachTo(); if (debugAttachedTo == null || debugAttachedTo.isEmpty()) { return; } else { types = ProfilingViewOptionsDialog.getProfileTypes(ria); } if (!isValidProfileId(types, _whatToProfile)) { _whatToProfile = types[0].getId(); } _methodTimeType = ps.getBoolean(PreferenceConstants.NET_RIM_EJDE_UI_VIEWS_METHOD_TIME_TYPE); log.debug("Profile option - type:" + _whatToProfile + ", method: " + (_methodTimeType ? "Cumulative" : "In method only")); ria.profileSetType(_whatToProfile); updateTypeColumeTitle(); _isInitialized = true; } catch (Exception e) { log.error("", e); } } } protected void handleRIMDebugEvent(DebugEvent event) { if (event.getKind() == DebugEvent.CREATE && !_isInitialized) { initProfileParameters(RIA.getCurrentDebugger()); } } public void createTableViewPart(Composite parent) { GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 1; parent.setLayout(gridLayout); // create a TabFolder component on the view _tabFolder = new TabFolder(parent, SWT.BOTTOM); _tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH)); _profileTabs = new ProfileTab[3]; _profileTabs[INDEX_OF_TAB_SUMMARY] = new SummaryProfileTab(this); _profileTabs[INDEX_OF_TAB_METHOD] = new MethodProfileTab(this); _profileTabs[INDEX_OF_TAB_SOURCE] = new SourceProfileTab(this); // if (!DebugUtils.isRIMDebuggerRunning()) { setMessage(Messages.ProcessView_NO_BB_DEBUG_SESSION_MSG, true); } } /** * Set focus to a certain UI component. */ public void setFocus() { // nothing to do } /** * Gets the profile type that need to be displayed. * * @return Profile type. */ public int getWhatToProfile() { return _whatToProfile; } /** * Gets method time type. * * @return <code>true</code> method time will be cumulated, <code>false</code> otherwise. */ public boolean getMethodTimeType() { return _methodTimeType; } /** * Set the tab at the given zero-relative index in the tabfolder as selected. * * @param index * the index of the item to select. */ public void setActiveTab(int index) { _tabFolder.setSelection(index); } protected TabFolder getTabFolder() { return _tabFolder; } /** * Updates the title of the type column (what to profile) of each tab. */ void updateTypeColumeTitle() { Display.getDefault().syncExec(new Runnable() { @Override public void run() { // the _profileTabs could be null if this is called during the view creation if (_profileTabs == null) { return; } for (int i = 0; i < _profileTabs.length; i++) { _profileTabs[i].updateTypeColumeTitle(); } } }); } /** * Display profile data. */ public void displayProfileData(ProfileTab[] tabs) { if (tabs == null || tabs.length == 0) return; if (_pd != null) { for (int i = 0; i < tabs.length; i++) tabs[i].displayData(_pd); } } /** * Display source code information of <code>pi</code>. * * @param pi * An instance of ProfileItem. */ protected void displaySourceData(ProfileItem pi) { SourceProfileTab sourceTab = (SourceProfileTab) _profileTabs[INDEX_OF_TAB_SOURCE]; sourceTab.setTotal(_pd.getTotalExecutionTicks()); sourceTab.setHistory(pi); sourceTab.clearExpansion(); sourceTab.displayData(pi); setActiveTab(INDEX_OF_TAB_SOURCE); } /** * Clears the view (all tabs). * * @param clearPreferences * <code>true</code> record or the last operation on this tab will be cleaned; <code>false</code> record or the * last operation on this tab will not be cleaned. */ public void clearVeiwer(boolean clearPreferences) { if (_pd != null) // clear display on each tab for (int i = 0; i < _profileTabs.length; i++) _profileTabs[i].clearTab(clearPreferences); } /** * Save profile data to a csv file. */ private void saveProfile() { if (_pd == null) return; try { // save profile data to the file saveContents(RimIDEUtil.openCSVFileForSave(getSite().getShell())); } catch (IDEError e) { log.error("", e); } } /** * Writes the profile data to <code>file</code>. * * @param file * Destination file. * @throws IDEError */ private void saveContents(File file) throws IDEError { if (file == null) { return; } RIA ria = RIA.getCurrentDebugger(); if (ria == null) { return; } String debugAttachedTo = ria.getDebugAttachTo(); if (debugAttachedTo == null || debugAttachedTo.isEmpty()) { return; } PrintStream out = null; try { out = new PrintStream(new FileOutputStream(file)); out.print(RIA.getString("ProfileCSVFileHeader1")); //$NON-NLS-1$ out.print(ria.profileGetTypes()[_whatToProfile].getDescription()); out.print(RIA.getString("ProfileCSVFileHeader2")); //$NON-NLS-1$ out.println(); ProfileItem[] modules = sortedElements(_pd, null); for (int i = 0; i < modules.length; i++) { ProfileItem module = modules[i]; Object moduleName = module; ProfileItem[] methods = sortedElements(module, null); for (int j = 0; j < methods.length; j++) { ProfileItem method = methods[j]; out.print(moduleName); out.print(", "); //$NON-NLS-1$ String methodStr = method.toString(); Object handle = method.getMethodHandle(); if (handle != null && handle instanceof DebugMethod) { methodStr = ((DebugMethod) handle).getFullName(); } out.print(Util.replace(methodStr, ",", "")); //$NON-NLS-1$ //$NON-NLS-2$ out.print(", "); //$NON-NLS-1$ out.print(method.getTicks()); out.print(", "); //$NON-NLS-1$ out.print(method.getCount()); out.println(); } } out.close(); } catch (IOException e) { log.error("", e); } } /** * Sorts children ProfileItems of <code>source</code>. * * @param source * ProfileItemSource instance. * @param comparator * Comparator instance used to sort children items of <code>source</code>. * @return Array of sorted children ProfileItems of <code>source</code>. */ protected static ProfileItem[] sortedElements(ProfileItemSource source, Comparator comparator) { ProfileItem profileItems[] = getUnsortedElements(source); if (profileItems.length == 0) return profileItems; if ((source instanceof ProfileMethod) || (source instanceof ProfileLine)) // source lines are sorted use ProfileMethod's default comparator Arrays.sort(profileItems, source.getComparator()); else Arrays.sort(profileItems, comparator == null ? source.getComparator() : comparator); return profileItems; } /** * Gets unsorted children of given <code>source</code>. * * @param source * @return */ protected static ProfileItem[] getUnsortedElements(ProfileItemSource source) { Enumeration enumeration = source.getChildrenKeys(); if (enumeration == null) return new ProfileItem[0]; ProfileItem profileItems[] = new ProfileItem[source.getChildCount()]; int i = 0; while (enumeration.hasMoreElements()) { ProfileItem pi = source.getChild(enumeration.nextElement()); profileItems[i++] = pi; } return profileItems; } private void refresh(boolean clearPreferences) throws CoreException { RefreshProfilerViewJob job = new RefreshProfilerViewJob(); try { PlatformUI.getWorkbench().getProgressService().run(false, true, job); } catch (InvocationTargetException e) { log.error("", e); } catch (InterruptedException e) { log.error("", e); } } class RefreshProfilerViewJob implements IRunnableWithProgress { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask(Messages.ProfilerView_Refresh, 100); RIA ria = RIA.getCurrentDebugger(); if (ria == null) { return; } clearVeiwer(true); monitor.worked(10); try { ria.profileRefreshData(); monitor.worked(20); _pd = ria.profileGetData(); monitor.worked(30); if (_pd == null) return; ProfileItem.setTickMode(_methodTimeType); monitor.worked(40); displayProfileData(new ProfileTab[] { _profileTabs[INDEX_OF_TAB_SUMMARY], _profileTabs[INDEX_OF_TAB_METHOD], _profileTabs[INDEX_OF_TAB_SOURCE] }); monitor.worked(50); setHasData(true); monitor.worked(60); updateToolbar(); } catch (IDEError e) { log.error("", e); } monitor.done(); } } // ------ Methods in BasicDebugView to be overridden ------ /** * RIM Debug session is terminated. * * @see BasicDebugView#RIMDebugTerminated(). */ public void RIMDebugTerminated(ILaunch[] launches) { setMessage(Messages.ProcessView_NO_BB_DEBUG_SESSION_MSG, true); _isInitialized = false; this.getSite().getShell().getDisplay().syncExec(new Runnable() { @Override public void run() { clear(); } }); // this.getSite().getShell().getDisplay().asyncExec( new CloseViewJob( this ) ); } /** * Gets new profile data, erases the current profile data, and display the new profile data. * * @throws CoreException * @see BasicDebugView#refresh(). * */ public void refresh() throws CoreException { refresh(true); } public void forward() { if (!(_tabFolder.getSelectionIndex() == INDEX_OF_TAB_SOURCE)) return; _profileTabs[INDEX_OF_TAB_SOURCE].forward(); } public void backward() { if (!(_tabFolder.getSelectionIndex() == INDEX_OF_TAB_SOURCE)) return; _profileTabs[INDEX_OF_TAB_SOURCE].backward(); } /** * (non-Javadoc) * * @see BasicDebugView#clear(). */ public void clear() { // clear the debugger RIA ria = RIA.getCurrentDebugger(); if (ria == null) { return; } try { ria.profileClearData(); } catch (IDEError e) { log.error(e); return; } // clear the display data clearVeiwer(true); _pd = null; setHasData(false); updateToolbar(); updateTypeColumeTitle(); } /** * Gets the ProfileData instance represented by this view. * * @return a ProfileData instance. */ public ProfileData getProfileData() { return _pd; } /** * (non-Javadoc) * * @see BasicDebugView#save(). */ public void save() { saveProfile(); } public void dispose() { super.dispose(); if (RIA.getCurrentDebugger() != null) { RIA.getCurrentDebugger().setProfileEnabled(false); } _pd = null; _profileTabs = null; } private static boolean isValidProfileId(ProfileType[] types, int id) { for (int i = 0; i < types.length; i++) { if (types[i].getId() == id) { return true; } } return false; } /** * Opens an option dialog. Options related to profiler can be set on the dialog and will be saved as references when "OK" * button is pressed. * * @see BasicDebugView#setOptions(). */ public void setOptions() { // create an ImplicitBuildRuleEditDialog instance ProfilingViewOptionsDialog optionsDialog = new ProfilingViewOptionsDialog(getSite().getShell()); // show the dialog optionsDialog.open(); if (optionsDialog.isOkButtonClicked()) { try { IPreferenceStore ps = ContextManager.PLUGIN.getPreferenceStore(); int whatToProfile = ps.getInt(PreferenceConstants.NET_RIM_EJDE_UI_VIEWS_WHATTOPROFILE); if (whatToProfile != _whatToProfile) { initProfileParameters(RIA.getCurrentDebugger()); clear(); enableActions(REFRESH_BUTTON, false); } else { initProfileParameters(RIA.getCurrentDebugger()); refresh(false); } } catch (Exception e) { log.error("", e); } } } /** * Save view content to a XML file. * <p> * <b>subclasses need to override this method.</b> */ public void saveXML() { File xmlFile = chooseDataFile(); if (xmlFile == null) { return; } if (!xmlFile.exists()) { xmlFile = ProjectUtils.createFile(xmlFile); if (xmlFile == null || !xmlFile.exists()) { return; } } ProgressMonitorDialog dialog = new ProgressMonitorDialog(ContextManager.getActiveWorkbenchShell()); SaveDataRunnale runnable = new SaveDataRunnale(xmlFile, this); try { dialog.run(false, true, runnable); } catch (InvocationTargetException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), e.getMessage(), Messages.ErrorHandler_DIALOG_TITLE); } catch (InterruptedException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), e.getMessage(), Messages.ErrorHandler_DIALOG_TITLE); } } /** * Save raw data of the view content to a XML file. * <p> * <b>subclasses need to override this method.</b> */ public void saveRawToXML() { File xmlFile = chooseRawDataFile(); if (xmlFile == null) { return; } if (!xmlFile.exists()) { xmlFile = ProjectUtils.createFile(xmlFile); if (xmlFile == null || !xmlFile.exists()) { return; } } ProgressMonitorDialog dialog = new ProgressMonitorDialog(ContextManager.getActiveWorkbenchShell()); SaveRawDataRunnale runnable = new SaveRawDataRunnale(xmlFile, this); try { dialog.run(false, true, runnable); } catch (InvocationTargetException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), e.getMessage(), Messages.ErrorHandler_DIALOG_TITLE); } catch (InterruptedException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), e.getMessage(), Messages.ErrorHandler_DIALOG_TITLE); } } private void saveRawData(File xmlFile) { RIA ria = RIA.getCurrentDebugger(); if (ria == null) { return; } if (xmlFile == null) { return; } if (!xmlFile.exists()) { xmlFile = ProjectUtils.createFile(xmlFile); if (xmlFile == null || !xmlFile.exists()) { return; } } PrintStream out = null; try { out = new PrintStream(new FileOutputStream(xmlFile)); ria.profileDumpRawXML(out); } catch (Exception e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), Messages.ErrorHandler_DIALOG_TITLE, e.getMessage()); } finally { if (out != null) { out.close(); } } } private File chooseDataFile() { return chooseXMLFile(new String[] { "*.xml" }, new String[] { "XML File (*.xml)" }); } private File chooseRawDataFile() { return chooseXMLFile(new String[] { "*.xml", "*.bbprof" }, new String[] { "XML File (*.xml)", "BlackBerry ProfileVisDesktop File (*.bbprof)" }); } private File chooseXMLFile(String[] filterExtensions, String[] filterNamesExtensions) { FileDialog dialog = new FileDialog(this.getSite().getShell(), SWT.SAVE); dialog.setFilterExtensions(filterExtensions); dialog.setFilterNames(filterNamesExtensions); String xmlFile = dialog.open(); if (!StringUtils.isBlank(xmlFile)) { return new File(xmlFile); } return null; } @Override public String resolveSourceLine(ProfileItem item) { RIA ria = RIA.getCurrentDebugger(); if (ria == null) { return Messages.SourceProfileTab_NO_SOURCE_MESSAGE; } ProfileSourceLine psl = item.getLineHandle(); if (psl == null) return Messages.SourceProfileTab_NO_SOURCE_MESSAGE; Object line = psl.getLine(); if (line == null) { return Messages.SourceProfileTab_NO_SOURCE_MESSAGE; } return line.toString(); } public void openProfileVis() { File tmpFile = getTmpFile(); ProgressMonitorDialog dialog = new ProgressMonitorDialog(ContextManager.getActiveWorkbenchShell()); SaveRawDataRunnale runnable = new SaveRawDataRunnale(tmpFile, this); try { dialog.run(false, true, runnable); } catch (InvocationTargetException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), e.getMessage(), Messages.ErrorHandler_DIALOG_TITLE); return; } catch (InterruptedException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), e.getMessage(), Messages.ErrorHandler_DIALOG_TITLE); return; } try { Desktop.getDesktop().open(tmpFile); } catch (IOException e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), Messages.ErrorHandler_DIALOG_TITLE, Messages.BBProfileVis_Not_Installed_ErrMsg); } } private static synchronized File getTmpFile() { String tmpDir = System.getProperty("java.io.tmpdir"); if (tmpDir == null) { // Shouldn't happen tmpDir = "."; } File file = null; for (int retry = 0; retry < 10; retry++) { file = new File(tmpDir, BLACKBERRY_PROFILEVISDESKTOP_TMP_FILE_PREFIX + System.currentTimeMillis() + BLACKBERRY_PROFILEVISDESKTOP_TMP_FILE_SUFFIX); if (!file.exists()) { return file; } } throw new RuntimeException("unable to create temporary file"); } // ----- Inner Classes ------ class CloseViewJob implements Runnable { ProfilerView _view; CloseViewJob(ProfilerView view) { _view = view; } public void run() { IViewSite viewSite = _view.getViewSite(); if (viewSite == null) { return; } IWorkbenchPage workbenchPage = viewSite.getPage(); if (workbenchPage == null) { return; } workbenchPage.hideView(_view); } } class SaveRawDataRunnale implements IRunnableWithProgress { File destFile; SourceResolver sourceResolver; public SaveRawDataRunnale(File destFile, SourceResolver sourceResolver) { this.sourceResolver = sourceResolver; this.destFile = destFile; } @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { monitor.beginTask("Saving profiling raw data...", 10); log.trace("Save raw to XML"); monitor.worked(1); saveRawData(destFile); } finally { monitor.done(); } } } class SaveDataRunnale implements IRunnableWithProgress { File destFile; SourceResolver sourceResolver; public SaveDataRunnale(File destFile, SourceResolver sourceResolver) { this.sourceResolver = sourceResolver; this.destFile = destFile; } @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { monitor.beginTask("Saving profiling data...", 10); log.trace("Save XML"); RIA ria = RIA.getCurrentDebugger(); if (ria == null) { return; } monitor.worked(1); ProfileData profileData = getProfileData(); if (profileData == null) { return; } monitor.worked(1); try { profileData.saveContentsInXml(destFile, ria.profileGetTypes()[getWhatToProfile()].getDescription(), this.sourceResolver); } catch (IDEError e) { log.error(e); MessageDialog.openError(ContextManager.getActiveWorkbenchShell(), Messages.ErrorHandler_DIALOG_TITLE, e.getMessage()); } } finally { monitor.done(); } } } }