diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace')
8 files changed, 1259 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java new file mode 100644 index 000000000..f0e080421 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptions.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +public interface ISystraceOptions { + /** Get tags to enable, returns null if no tags need to be enabled. */ + String getTags(); + + /** Get the command line options to atrace. */ + String getOptions(); +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java new file mode 100644 index 000000000..4cc0faa59 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/ISystraceOptionsDialog.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +public interface ISystraceOptionsDialog { + ISystraceOptions getSystraceOptions(); + String getTraceFilePath(); + int open(); +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java new file mode 100644 index 000000000..b462ada29 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV1.java @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import java.io.File; + +public class SystraceOptionsDialogV1 extends TitleAreaDialog implements ISystraceOptionsDialog { + private static final String TITLE = "Android System Trace"; + private static final String DEFAULT_MESSAGE = + "Settings to use while capturing system level trace"; + private static final String DEFAULT_TRACE_FNAME = "trace.html"; //$NON-NLS-1$ + + private Text mDestinationText; + private String mDestinationPath; + private Text mTraceDurationText; + private Text mTraceBufferSizeText; + + private static String sSaveToFolder = System.getProperty("user.home"); //$NON-NLS-1$ + private static String sTraceDuration = ""; + private static String sTraceBufferSize = ""; + + private Button mTraceCpuFreqBtn; + private Button mTraceCpuIdleBtn; + private Button mTraceCpuLoadBtn; + private Button mTraceDiskIoBtn; + private Button mTraceKernelWorkqueuesBtn; + private Button mTraceCpuSchedulerBtn; + + private static boolean sTraceCpuFreq; + private static boolean sTraceCpuIdle; + private static boolean sTraceCpuLoad; + private static boolean sTraceDiskIo; + private static boolean sTraceKernelWorkqueues; + private static boolean sTraceCpuScheduler; + + private Button mGfxTagBtn; + private Button mInputTagBtn; + private Button mViewTagBtn; + private Button mWebViewTagBtn; + private Button mWmTagBtn; + private Button mAmTagBtn; + private Button mSyncTagBtn; + private Button mAudioTagBtn; + private Button mVideoTagBtn; + private Button mCameraTagBtn; + + private static boolean sGfxTag; + private static boolean sInputTag; + private static boolean sViewTag; + private static boolean sWebViewTag; + private static boolean sWmTag; + private static boolean sAmTag; + private static boolean sSyncTag; + private static boolean sAudioTag; + private static boolean sVideoTag; + private static boolean sCameraTag; + + private final SystraceOptions mOptions = new SystraceOptions(); + + public SystraceOptionsDialogV1(Shell parentShell) { + super(parentShell); + } + + @Override + protected Control createDialogArea(Composite parent) { + setTitle(TITLE); + setMessage(DEFAULT_MESSAGE); + + Composite c = new Composite(parent, SWT.BORDER); + c.setLayout(new GridLayout(3, false)); + c.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label l = new Label(c, SWT.NONE); + l.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + l.setText("Destination File: "); + + mDestinationText = new Text(c, SWT.BORDER); + mDestinationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mDestinationText.setText(sSaveToFolder + File.separator + DEFAULT_TRACE_FNAME); + + final Button browse = new Button(c, SWT.NONE); + browse.setText("Browse..."); + browse.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + String path = openBrowseDialog(browse.getShell()); + if (path != null) mDestinationText.setText(path); + } + }); + + Label lblTraceDurationseconds = new Label(c, SWT.NONE); + lblTraceDurationseconds.setLayoutData( + new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblTraceDurationseconds.setText("Trace duration (seconds): "); + + mTraceDurationText = new Text(c, SWT.BORDER); + mTraceDurationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mTraceDurationText.setText(sTraceDuration); + + Label lblTraceBufferSize = new Label(c, SWT.NONE); + lblTraceBufferSize.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblTraceBufferSize.setText("Trace Buffer Size (kb): "); + + mTraceBufferSizeText = new Text(c, SWT.BORDER); + mTraceBufferSizeText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mTraceBufferSizeText.setText(sTraceBufferSize); + + Label separator = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + separator.setLayoutData(gd); + + Group grpTraceEvents = new Group(c, SWT.BORDER); + grpTraceEvents.setLayout(new GridLayout(3, false)); + grpTraceEvents.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1)); + grpTraceEvents.setText("Trace Events"); + + mTraceCpuFreqBtn = new Button(grpTraceEvents, SWT.CHECK); + mTraceCpuFreqBtn.setText("CPU Frequency Changes"); + mTraceCpuFreqBtn.setSelection(sTraceCpuFreq); + + mTraceCpuIdleBtn = new Button(grpTraceEvents, SWT.CHECK); + mTraceCpuIdleBtn.setText("CPU Idle Events"); + mTraceCpuIdleBtn.setSelection(sTraceCpuIdle); + + mTraceCpuLoadBtn = new Button(grpTraceEvents, SWT.CHECK); + mTraceCpuLoadBtn.setText("CPU Load"); + mTraceCpuLoadBtn.setSelection(sTraceCpuLoad); + + mTraceCpuSchedulerBtn = new Button(grpTraceEvents, SWT.CHECK); + mTraceCpuSchedulerBtn.setText("CPU Scheduler"); + mTraceCpuSchedulerBtn.setSelection(sTraceCpuScheduler); + + Group grpTraceRootEvents = new Group(c, SWT.BORDER); + grpTraceRootEvents.setLayout(new GridLayout(2, false)); + grpTraceRootEvents.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1)); + grpTraceRootEvents.setText("Trace Events that require root privileges on device"); + + mTraceDiskIoBtn = new Button(grpTraceRootEvents, SWT.CHECK); + mTraceDiskIoBtn.setText("Disk I/O"); + mTraceDiskIoBtn.setSelection(sTraceDiskIo); + + mTraceKernelWorkqueuesBtn = new Button(grpTraceRootEvents, SWT.CHECK); + mTraceKernelWorkqueuesBtn.setText("Kernel Workqueues (requires root)"); + mTraceKernelWorkqueuesBtn.setSelection(sTraceKernelWorkqueues); + + Group grpTraceTags = new Group(c, SWT.BORDER); + grpTraceTags.setLayout(new GridLayout(5, false)); + grpTraceTags.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 3, 1)); + grpTraceTags.setText("Trace Tags"); + + mGfxTagBtn = new Button(grpTraceTags, SWT.CHECK); + mGfxTagBtn.setText("gfx"); + mGfxTagBtn.setSelection(sGfxTag); + + mInputTagBtn = new Button(grpTraceTags, SWT.CHECK); + mInputTagBtn.setText("input"); + mInputTagBtn.setSelection(sInputTag); + + mViewTagBtn = new Button(grpTraceTags, SWT.CHECK); + mViewTagBtn.setText("view"); + mViewTagBtn.setSelection(sViewTag); + + mWebViewTagBtn = new Button(grpTraceTags, SWT.CHECK); + mWebViewTagBtn.setText("webview"); + mWebViewTagBtn.setSelection(sWebViewTag); + + mWmTagBtn = new Button(grpTraceTags, SWT.CHECK); + mWmTagBtn.setText("wm"); + mWmTagBtn.setSelection(sWmTag); + + mAmTagBtn = new Button(grpTraceTags, SWT.CHECK); + mAmTagBtn.setText("am"); + mAmTagBtn.setSelection(sAmTag); + + mSyncTagBtn = new Button(grpTraceTags, SWT.CHECK); + mSyncTagBtn.setText("sync"); + mSyncTagBtn.setSelection(sSyncTag); + + mAudioTagBtn = new Button(grpTraceTags, SWT.CHECK); + mAudioTagBtn.setText("audio"); + mAudioTagBtn.setSelection(sAudioTag); + + mVideoTagBtn = new Button(grpTraceTags, SWT.CHECK); + mVideoTagBtn.setText("video"); + mVideoTagBtn.setSelection(sVideoTag); + + mCameraTagBtn = new Button(grpTraceTags, SWT.CHECK); + mCameraTagBtn.setText("camera"); + mCameraTagBtn.setSelection(sCameraTag); + + Label lblTraceTagsWarning = new Label(grpTraceTags, SWT.NONE); + lblTraceTagsWarning.setText( + "Changes to trace tags will likely need a restart of the Android framework to take effect:\n" + + " $ adb shell stop\n" + + " $ adb shell start"); + lblTraceTagsWarning.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 5, 1)); + + ModifyListener m = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + validateFields(); + } + }; + + mDestinationText.addModifyListener(m); + mTraceBufferSizeText.addModifyListener(m); + mTraceDurationText.addModifyListener(m); + + return c; + } + + private void validateFields() { + // validate trace destination path + String msg = validatePath(mDestinationText.getText()); + if (msg != null) { + setErrorMessage(msg); + getButton(OK).setEnabled(false); + return; + } + + // validate the trace duration + if (!validateInteger(mTraceDurationText.getText())) { + setErrorMessage("Trace Duration should be a valid integer (seconds)"); + getButton(OK).setEnabled(false); + return; + } + + // validate the trace buffer size + if (!validateInteger(mTraceBufferSizeText.getText())) { + setErrorMessage("Trace Buffer Size should be a valid integer (kilobytes)"); + getButton(OK).setEnabled(false); + return; + } + + getButton(OK).setEnabled(true); + setErrorMessage(null); + } + + private boolean validateInteger(String text) { + if (text == null || text.isEmpty()) { + return true; + } + + try { + Integer.parseInt(text); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + private String validatePath(String path) { + if (path == null || path.isEmpty()) { + return null; + } + + File f = new File(path); + if (f.isDirectory()) { + return String.format("The path '%s' points to a folder", path); + } + + if (!f.exists()) { // if such a file doesn't exist, make sure the parent folder is valid + if (!f.getParentFile().isDirectory()) { + return String.format("That path '%s' is not a valid folder.", f.getParent()); + } + } + + return null; + } + + private String openBrowseDialog(Shell parentShell) { + FileDialog fd = new FileDialog(parentShell, SWT.SAVE); + + fd.setText("Save To"); + fd.setFileName(DEFAULT_TRACE_FNAME); + + fd.setFilterPath(sSaveToFolder); + fd.setFilterExtensions(new String[] { "*.html" }); //$NON-NLS-1$ + + String fname = fd.open(); + if (fname == null || fname.trim().length() == 0) { + return null; + } + + sSaveToFolder = fd.getFilterPath(); + return fname; + } + + @Override + protected void okPressed() { + mDestinationPath = mDestinationText.getText().trim(); + + sTraceDuration = mTraceDurationText.getText(); + if (!sTraceDuration.isEmpty()) { + mOptions.mTraceDuration = Integer.parseInt(sTraceDuration); + } + + sTraceBufferSize = mTraceBufferSizeText.getText(); + if (!sTraceBufferSize.isEmpty()) { + mOptions.mTraceBufferSize = Integer.parseInt(sTraceBufferSize); + } + + mOptions.mTraceCpuFreq = mTraceCpuFreqBtn.getSelection(); + mOptions.mTraceCpuIdle = mTraceCpuIdleBtn.getSelection(); + mOptions.mTraceCpuLoad = mTraceCpuLoadBtn.getSelection(); + mOptions.mTraceDiskIo = mTraceDiskIoBtn.getSelection(); + mOptions.mTraceKernelWorkqueues = mTraceKernelWorkqueuesBtn.getSelection(); + mOptions.mTraceCpuScheduler = mTraceCpuSchedulerBtn.getSelection(); + + if (mGfxTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_GFX); + if (mInputTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_INPUT); + if (mViewTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_VIEW); + if (mWebViewTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_WEBVIEW); + if (mWmTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_WM); + if (mAmTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_AM); + if (mSyncTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_SYNC); + if (mAudioTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_AUDIO); + if (mVideoTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_VIDEO); + if (mCameraTagBtn.getSelection()) mOptions.enableTag(SystraceOptions.TAG_CAMERA); + + // save current selections to be restored if the dialog is invoked again + sTraceCpuFreq = mTraceCpuFreqBtn.getSelection(); + sTraceCpuIdle = mTraceCpuIdleBtn.getSelection(); + sTraceCpuLoad = mTraceCpuLoadBtn.getSelection(); + sTraceDiskIo = mTraceDiskIoBtn.getSelection(); + sTraceKernelWorkqueues = mTraceKernelWorkqueuesBtn.getSelection(); + sTraceCpuScheduler = mTraceCpuSchedulerBtn.getSelection(); + + sGfxTag = mGfxTagBtn.getSelection(); + sInputTag = mInputTagBtn.getSelection(); + sViewTag = mViewTagBtn.getSelection(); + sWebViewTag = mWebViewTagBtn.getSelection(); + sWmTag = mWmTagBtn.getSelection(); + sAmTag = mAmTagBtn.getSelection(); + sSyncTag = mSyncTagBtn.getSelection(); + sAudioTag = mAudioTagBtn.getSelection(); + sVideoTag = mVideoTagBtn.getSelection(); + sCameraTag = mCameraTagBtn.getSelection(); + + super.okPressed(); + } + + @Override + public SystraceOptions getSystraceOptions() { + return mOptions; + } + + @Override + public String getTraceFilePath() { + return mDestinationPath; + } + + private class SystraceOptions implements ISystraceOptions { + // This list is based on the tags in frameworks/native/include/utils/Trace.h + private static final int TAG_GFX = 1 << 1; + private static final int TAG_INPUT = 1 << 2; + private static final int TAG_VIEW = 1 << 3; + private static final int TAG_WEBVIEW = 1 << 4; + private static final int TAG_WM = 1 << 5; + private static final int TAG_AM = 1 << 6; + private static final int TAG_SYNC = 1 << 7; + private static final int TAG_AUDIO = 1 << 8; + private static final int TAG_VIDEO = 1 << 9; + private static final int TAG_CAMERA = 1 << 10; + + private int mTraceBufferSize; + private int mTraceDuration; + + private boolean mTraceCpuFreq; + private boolean mTraceCpuIdle; + private boolean mTraceCpuLoad; + private boolean mTraceDiskIo; + private boolean mTraceKernelWorkqueues; + private boolean mTraceCpuScheduler; + + private int mTag; + + private void enableTag(int tag) { + mTag |= tag; + } + + @Override + public String getTags() { + return mTag == 0 ? null : "0x" + Integer.toHexString(mTag); + } + + @Override + public String getOptions() { + StringBuilder sb = new StringBuilder(20); + + if (mTraceCpuFreq) sb.append("-f "); //$NON-NLS-1$ + if (mTraceCpuIdle) sb.append("-i "); //$NON-NLS-1$ + if (mTraceCpuLoad) sb.append("-l "); //$NON-NLS-1$ + if (mTraceDiskIo) sb.append("-d "); //$NON-NLS-1$ + if (mTraceKernelWorkqueues) sb.append("-w "); //$NON-NLS-1$ + if (mTraceCpuScheduler) sb.append("-s "); //$NON-NLS-1$ + + if (mTraceDuration > 0) { + sb.append("-t"); //$NON-NLS-1$ + sb.append(mTraceDuration); + sb.append(' '); + } + + if (mTraceBufferSize > 0) { + sb.append("-b "); //$NON-NLS-1$ + sb.append(mTraceBufferSize); + sb.append(' '); + } + + return sb.toString().trim(); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java new file mode 100644 index 000000000..e28edd389 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOptionsDialogV2.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +import com.android.ddmuilib.TableHelper; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Text; + +import java.io.File; +import java.util.List; +import java.util.Set; + +public class SystraceOptionsDialogV2 extends TitleAreaDialog implements ISystraceOptionsDialog { + private static final String TITLE = "Systrace (Android System Trace)"; + private static final String DEFAULT_MESSAGE = + "Settings to use while capturing system level trace"; + private static final String DEFAULT_TRACE_FNAME = "trace.html"; //$NON-NLS-1$ + private static final Set<String> sCommonTags = ImmutableSet.of( + "am", "app", "dalvik", "disk", "gfx", "input", "res", "sched", "view", "webview", "wm"); + + private Text mDestinationText; + private String mDestinationPath; + private Text mTraceDurationText; + private Text mTraceBufferSizeText; + private Combo mTraceAppCombo; + + private static String sSaveToFolder = System.getProperty("user.home"); //$NON-NLS-1$ + private static String sTraceDuration = "5"; + private static String sTraceBufferSize = "2048"; + private static Set<String> sEnabledTags = Sets.newHashSet(sCommonTags); + private static String sLastSelectedApp = null; + + private final List<SystraceTag> mCommonSupportedTags; + private final List<SystraceTag> mAdvancedSupportedTags; + + private final List<String> mCurrentApps; + + private final SystraceOptions mOptions = new SystraceOptions(); + private Table mCommonTagsTable; + private Table mAdvancedTagsTable; + + public SystraceOptionsDialogV2(Shell parentShell, List<SystraceTag> tags, List<String> apps) { + super(parentShell); + mCurrentApps = apps; + + mCommonSupportedTags = Lists.newArrayListWithExpectedSize(tags.size()); + mAdvancedSupportedTags = Lists.newArrayListWithExpectedSize(tags.size()); + + for (SystraceTag supportedTag : tags) { + if (sCommonTags.contains(supportedTag.tag)) { + mCommonSupportedTags.add(supportedTag); + } else { + mAdvancedSupportedTags.add(supportedTag); + } + } + } + + @Override + protected Control createDialogArea(Composite parent) { + setTitle(TITLE); + setMessage(DEFAULT_MESSAGE); + + Composite c = new Composite(parent, SWT.BORDER); + c.setLayout(new GridLayout(3, false)); + c.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label l = new Label(c, SWT.NONE); + l.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + l.setText("Destination File: "); + + mDestinationText = new Text(c, SWT.BORDER); + mDestinationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mDestinationText.setText(sSaveToFolder + File.separator + DEFAULT_TRACE_FNAME); + + final Button browse = new Button(c, SWT.NONE); + browse.setText("Browse..."); + browse.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + String path = openBrowseDialog(browse.getShell()); + if (path != null) mDestinationText.setText(path); + } + }); + + Label lblTraceDurationseconds = new Label(c, SWT.NONE); + lblTraceDurationseconds.setLayoutData( + new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblTraceDurationseconds.setText("Trace duration (seconds): "); + + mTraceDurationText = new Text(c, SWT.BORDER); + mTraceDurationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mTraceDurationText.setText(sTraceDuration); + + Label lblTraceBufferSize = new Label(c, SWT.NONE); + lblTraceBufferSize.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblTraceBufferSize.setText("Trace Buffer Size (kb): "); + + mTraceBufferSizeText = new Text(c, SWT.BORDER); + mTraceBufferSizeText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mTraceBufferSizeText.setText(sTraceBufferSize); + + Label lblTraceAppName = new Label(c, SWT.NONE); + lblTraceAppName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblTraceAppName.setText("Enable Application Traces from: "); + + mTraceAppCombo = new Combo(c, SWT.DROP_DOWN | SWT.READ_ONLY); + mTraceAppCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + String[] items = new String[mCurrentApps.size() + 1]; + items[0] = "None"; + for (int i = 0; i < mCurrentApps.size(); i++) { + items[i+1] = mCurrentApps.get(i); + } + mTraceAppCombo.setItems(items); + if (sLastSelectedApp != null) { + mTraceAppCombo.setText(sLastSelectedApp); + } else { + mTraceAppCombo.select(0); + } + + Label separator = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + separator.setLayoutData(gd); + + ModifyListener m = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + validateFields(); + } + }; + + mDestinationText.addModifyListener(m); + mTraceBufferSizeText.addModifyListener(m); + mTraceDurationText.addModifyListener(m); + + mCommonTagsTable = createTable(c, "Commonly Used Tags: ", mCommonSupportedTags); + mAdvancedTagsTable = createTable(c, "Advanced Options: ", mAdvancedSupportedTags); + + return c; + } + + private Table createTable(Composite c, String label, List<SystraceTag> supportedTags) { + Label l = new Label(c, SWT.NONE); + l.setText(label); + l.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + + Table table = new Table(c, SWT.CHECK | SWT.BORDER); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.horizontalSpan = 2; + table.setLayoutData(gd); + table.setHeaderVisible(false); + table.setLinesVisible(false); + + for (SystraceTag tag : supportedTags) { + TableItem item = new TableItem(table, SWT.NONE); + item.setText(tag.info); + item.setChecked(sEnabledTags.contains(tag.tag)); + } + + TableHelper.createTableColumn(table, + "TagHeaderNotDisplayed", //$NON-NLS-1$ + SWT.LEFT, + "SampleTagForColumnLengthCalculation", //$NON-NLS-1$ + null, + null); + + return table; + } + + private void validateFields() { + // validate trace destination path + String msg = validatePath(mDestinationText.getText()); + if (msg != null) { + setErrorMessage(msg); + getButton(OK).setEnabled(false); + return; + } + + // validate the trace duration + if (!validateInteger(mTraceDurationText.getText())) { + setErrorMessage("Trace Duration should be a valid integer (seconds)"); + getButton(OK).setEnabled(false); + return; + } + + // validate the trace buffer size + if (!validateInteger(mTraceBufferSizeText.getText())) { + setErrorMessage("Trace Buffer Size should be a valid integer (kilobytes)"); + getButton(OK).setEnabled(false); + return; + } + + getButton(OK).setEnabled(true); + setErrorMessage(null); + } + + private boolean validateInteger(String text) { + if (text == null || text.isEmpty()) { + return true; + } + + try { + Integer.parseInt(text); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + private String validatePath(String path) { + if (path == null || path.isEmpty()) { + return null; + } + + File f = new File(path); + if (f.isDirectory()) { + return String.format("The path '%s' points to a folder", path); + } + + if (!f.exists()) { // if such a file doesn't exist, make sure the parent folder is valid + if (!f.getParentFile().isDirectory()) { + return String.format("That path '%s' is not a valid folder.", f.getParent()); + } + } + + return null; + } + + private String openBrowseDialog(Shell parentShell) { + FileDialog fd = new FileDialog(parentShell, SWT.SAVE); + + fd.setText("Save To"); + fd.setFileName(DEFAULT_TRACE_FNAME); + + fd.setFilterPath(sSaveToFolder); + fd.setFilterExtensions(new String[] { "*.html" }); //$NON-NLS-1$ + + String fname = fd.open(); + if (fname == null || fname.trim().length() == 0) { + return null; + } + + sSaveToFolder = fd.getFilterPath(); + return fname; + } + + @Override + protected void okPressed() { + mDestinationPath = mDestinationText.getText().trim(); + + sTraceDuration = mTraceDurationText.getText(); + if (!sTraceDuration.isEmpty()) { + mOptions.mTraceDuration = Integer.parseInt(sTraceDuration); + } + + sTraceBufferSize = mTraceBufferSizeText.getText(); + if (!sTraceBufferSize.isEmpty()) { + mOptions.mTraceBufferSize = Integer.parseInt(sTraceBufferSize); + } + + if (mTraceAppCombo.getSelectionIndex() != 0) { + mOptions.mTraceApp = sLastSelectedApp = mTraceAppCombo.getText(); + } + + sEnabledTags.clear(); + sEnabledTags.addAll(getEnabledTags(mCommonTagsTable, mCommonSupportedTags)); + sEnabledTags.addAll(getEnabledTags(mAdvancedTagsTable, mAdvancedSupportedTags)); + + super.okPressed(); + } + + private static Set<String> getEnabledTags(Table table, List<SystraceTag> tags) { + Set<String> enabledTags = Sets.newHashSetWithExpectedSize(tags.size()); + + for (int i = 0; i < table.getItemCount(); i++) { + TableItem it = table.getItem(i); + if (it.getChecked()) { + enabledTags.add(tags.get(i).tag); + } + } + + return enabledTags; + } + + @Override + public ISystraceOptions getSystraceOptions() { + return mOptions; + } + + @Override + public String getTraceFilePath() { + return mDestinationPath; + } + + private class SystraceOptions implements ISystraceOptions { + private int mTraceBufferSize; + private int mTraceDuration; + private String mTraceApp; + + @Override + public String getTags() { + return null; + } + + @Override + public String getOptions() { + StringBuilder sb = new StringBuilder(5 * mCommonSupportedTags.size()); + + if (mTraceApp != null) { + sb.append("-a "); //$NON-NLS-1$ + sb.append(mTraceApp); + sb.append(' '); + } + + if (mTraceDuration > 0) { + sb.append("-t"); //$NON-NLS-1$ + sb.append(mTraceDuration); + sb.append(' '); + } + + if (mTraceBufferSize > 0) { + sb.append("-b "); //$NON-NLS-1$ + sb.append(mTraceBufferSize); + sb.append(' '); + } + + for (String s : sEnabledTags) { + sb.append(s); + sb.append(' '); + } + + return sb.toString().trim(); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java new file mode 100644 index 000000000..2548edc23 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceOutputParser.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +import com.google.common.base.Charsets; +import com.google.common.io.Files; + +import java.io.File; +import java.io.IOException; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +/** {@link SystraceOutputParser} receives the output of atrace command run on the device, + * parses it and generates html based on the trace */ +public class SystraceOutputParser { + private static final String TRACE_START = "TRACE:\n"; //$NON-NLS-1$ + + private final boolean mUncompress; + private final String mJs; + private final String mCss; + private final String mHtmlPrefix; + private final String mHtmlSuffix; + + private byte[] mAtraceOutput; + private int mAtraceLength; + private int mSystraceIndex = -1; + + /** + * Constructs a atrace output parser. + * @param compressedStream Is the input stream compressed using zlib? + * @param systraceJs systrace javascript content + * @param systraceCss systrace css content + */ + public SystraceOutputParser(boolean compressedStream, String systraceJs, String systraceCss, + String htmlPrefix, String htmlSuffix) { + mUncompress = compressedStream; + mJs = systraceJs; + mCss = systraceCss; + mHtmlPrefix = htmlPrefix; + mHtmlSuffix = htmlSuffix; + } + + /** + * Parses the atrace output for systrace content. + * @param atraceOutput output bytes from atrace + */ + public void parse(byte[] atraceOutput) { + mAtraceOutput = atraceOutput; + mAtraceLength = atraceOutput.length; + + removeCrLf(); + + // locate the trace start marker within the first hundred bytes + String header = new String(mAtraceOutput, 0, Math.min(100, mAtraceLength)); + mSystraceIndex = locateSystraceData(header); + + if (mSystraceIndex < 0) { + throw new RuntimeException("Unable to find trace start marker 'TRACE:':\n" + header); + } + } + + /** Replaces \r\n with \n in {@link #mAtraceOutput}. */ + private void removeCrLf() { + int dst = 0; + for (int src = 0; src < mAtraceLength - 1; src++, dst++) { + byte copy; + if (mAtraceOutput[src] == '\r' && mAtraceOutput[src + 1] == '\n') { + copy = '\n'; + src++; + } else { + copy = mAtraceOutput[src]; + } + mAtraceOutput[dst] = copy; + } + + mAtraceLength = dst; + } + + private int locateSystraceData(String header) { + int index = header.indexOf(TRACE_START); + if (index < 0) { + return -1; + } else { + return index + TRACE_START.length(); + } + } + + public String getSystraceHtml() { + if (mSystraceIndex < 0) { + return ""; + } + + String trace = ""; + if (mUncompress) { + Inflater decompressor = new Inflater(); + decompressor.setInput(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex); + + byte[] buf = new byte[4096]; + int n; + StringBuilder sb = new StringBuilder(1000); + try { + while ((n = decompressor.inflate(buf)) > 0) { + sb.append(new String(buf, 0, n)); + } + } catch (DataFormatException e) { + throw new RuntimeException(e); + } + decompressor.end(); + + trace = sb.toString(); + } else { + trace = new String(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex); + } + + // each line should end with the characters \n\ followed by a newline + String html_out = trace.replaceAll("\n", "\\\\n\\\\\n"); + String header = String.format(mHtmlPrefix, mCss, mJs, ""); + String footer = mHtmlSuffix; + return header + html_out + footer; + } + + public static String getJs(File assetsFolder) { + try { + return String.format("<script language=\"javascript\">%s</script>", + Files.toString(new File(assetsFolder, "script.js"), Charsets.UTF_8)); + } catch (IOException e) { + return ""; + } + } + + public static String getCss(File assetsFolder) { + try { + return String.format("<style type=\"text/css\">%s</style>", + Files.toString(new File(assetsFolder, "style.css"), Charsets.UTF_8)); + } catch (IOException e) { + return ""; + } + } + + public static String getHtmlPrefix(File assetsFolder) { + return getHtmlTemplate(assetsFolder, "prefix.html"); + } + + public static String getHtmlSuffix(File assetsFolder) { + return getHtmlTemplate(assetsFolder, "suffix.html"); + } + + private static String getHtmlTemplate(File assetsFolder, String htmlFileName) { + try { + return Files.toString(new File(assetsFolder, htmlFileName), Charsets.UTF_8); + } catch (IOException e) { + return ""; + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java new file mode 100644 index 000000000..0fc03efa3 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTag.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +public class SystraceTag { + public final String tag; + public final String info; + + public SystraceTag(String tagName, String details) { + tag = tagName; + info = details; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java new file mode 100644 index 000000000..726296468 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceTask.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +import com.android.ddmlib.IDevice; +import com.android.ddmlib.IShellOutputReceiver; +import com.google.common.primitives.Bytes; + +public class SystraceTask implements Runnable { + private final IDevice mDevice; + private final String mOptions; + + private volatile boolean mCancel; + + private final Object mLock = new Object(); + private String errorMessage; + private boolean mTraceComplete; + private byte[] mBuffer = new byte[1024]; + private int mDataLength = 0; + + public SystraceTask(IDevice device, String options) { + mDevice = device; + mOptions = options; + } + + @Override + public void run() { + try { + mDevice.executeShellCommand("atrace " + mOptions, new Receiver(), 0); + } catch (Exception e) { + synchronized (mLock) { + errorMessage = "Unexpected error while running atrace on device: " + e; + } + } + } + + public void cancel() { + mCancel = true; + } + + public String getError() { + synchronized (mLock) { + return errorMessage; + } + } + + public byte[] getAtraceOutput() { + synchronized (mLock) { + return mTraceComplete ? mBuffer : null; + } + } + + private class Receiver implements IShellOutputReceiver { + @Override + public void addOutput(byte[] data, int offset, int length) { + synchronized (mLock) { + if (mDataLength + length > mBuffer.length) { + mBuffer = Bytes.ensureCapacity(mBuffer, mDataLength + length + 1, 1024); + } + + for (int i = 0; i < length; i++) { + mBuffer[mDataLength + i] = data[offset + i]; + } + mDataLength += length; + } + } + + @Override + public void flush() { + synchronized (mLock) { + // trim mBuffer to its final size + byte[] copy = new byte[mDataLength]; + for (int i = 0; i < mDataLength; i++) { + copy[i] = mBuffer[i]; + } + mBuffer = copy; + + mTraceComplete = true; + } + } + + @Override + public boolean isCancelled() { + return mCancel; + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java new file mode 100644 index 000000000..646a45412 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/systrace/SystraceVersionDetector.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.ddms.systrace; + +import com.android.ddmlib.CollectingOutputReceiver; +import com.android.ddmlib.IDevice; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.operation.IRunnableWithProgress; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SystraceVersionDetector implements IRunnableWithProgress { + public static final int SYSTRACE_V1 = 1; + public static final int SYSTRACE_V2 = 2; + + private final IDevice mDevice; + private List<SystraceTag> mTags; + + public SystraceVersionDetector(IDevice device) { + mDevice = device; + } + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, + InterruptedException { + monitor.beginTask("Checking systrace version on device..", + IProgressMonitor.UNKNOWN); + + CountDownLatch setTagLatch = new CountDownLatch(1); + CollectingOutputReceiver receiver = new CollectingOutputReceiver(setTagLatch); + try { + String cmd = "atrace --list_categories"; + mDevice.executeShellCommand(cmd, receiver); + setTagLatch.await(5, TimeUnit.SECONDS); + } catch (Exception e) { + throw new InvocationTargetException(e); + } + + String shellOutput = receiver.getOutput(); + mTags = parseSupportedTags(shellOutput); + + monitor.done(); + } + + public int getVersion() { + if (mTags == null) { + return SYSTRACE_V1; + } else { + return SYSTRACE_V2; + } + } + + public List<SystraceTag> getTags() { + return mTags; + } + + private List<SystraceTag> parseSupportedTags(String listCategoriesOutput) { + if (listCategoriesOutput == null) { + return null; + } + + if (listCategoriesOutput.contains("unknown option")) { + return null; + } + + String[] categories = listCategoriesOutput.split("\n"); + List<SystraceTag> tags = new ArrayList<SystraceTag>(categories.length); + + Pattern p = Pattern.compile("([^-]+) - (.*)"); //$NON-NLS-1$ + for (String category : categories) { + Matcher m = p.matcher(category); + if (m.find()) { + tags.add(new SystraceTag(m.group(1).trim(), m.group(2).trim())); + } + } + + return tags; + } +} |