diff options
Diffstat (limited to 'ddms')
8 files changed, 186 insertions, 106 deletions
diff --git a/ddms/app/src/com/android/ddms/UIThread.java b/ddms/app/src/com/android/ddms/UIThread.java index c98b3f1..7940c74 100644 --- a/ddms/app/src/com/android/ddms/UIThread.java +++ b/ddms/app/src/com/android/ddms/UIThread.java @@ -292,7 +292,6 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { mCurrentActivator.selectAll(); } } - } /** @@ -305,13 +304,15 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { super(parentShell); } - public void onFailure(final Client client) { + public void onEndFailure(final Client client, final String message) { mDisplay.asyncExec(new Runnable() { public void run() { try { - displayError("Unable to create HPROF file for application '%1$s'.\n" + + displayErrorFromUiThread( + "Unable to create HPROF file for application '%1$s'\n\n%2$s" + "Check logcat for more information.", - client.getClientData().getClientDescription()); + client.getClientData().getClientDescription(), + message != null ? message + "\n\n" : ""); } finally { // this will make sure the dump hprof button is re-enabled for the // current selection. as the client is finished dumping an hprof file @@ -333,16 +334,16 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { client.getClientData().getClientDescription() + ".hprof", remoteFilePath, "Save HPROF file"); if (result != null && result.getCode() != SyncService.RESULT_OK) { - displayError( + displayErrorFromUiThread( "Unable to download HPROF file from device '%1$s'.\n\n%2$s", device.getSerialNumber(), result.getMessage()); } } else { - displayError("Unable to download HPROF file from device '%1$s'.", + displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.", device.getSerialNumber()); } } catch (Exception e) { - displayError("Unable to download HPROF file from device '%1$s'.", + displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.", device.getSerialNumber()); } finally { @@ -354,9 +355,18 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { }); } - private void displayError(String format, Object... args) { - MessageDialog.openError(mParentShell, "HPROF Error", - String.format(format, args)); + public void onSuccess(final byte[] data, final Client client) { + mDisplay.asyncExec(new Runnable() { + public void run() { + promptAndSave(client.getClientData().getClientDescription() + ".hprof", data, + "Save HPROF file"); + } + }); + } + + @Override + protected String getDialogTitle() { + return "HPROF Error"; } } diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java index fa53bef..5991026 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java @@ -230,13 +230,13 @@ public class Client { * Makes the VM dump an HPROF file */ public void dumpHprof() { - boolean canStream = false; //mClientData.hasFeature(ClientData.FEATURE_HPROF_STREAMING); + boolean canStream = mClientData.hasFeature(ClientData.FEATURE_HPROF_STREAMING); try { if (canStream) { HandleHeap.sendHPDS(this); } else { - String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") + - ".hprof"; + String file = "/sdcard/" + mClientData.getClientDescription().replaceAll( + "\\:.*", "") + ".hprof"; HandleHeap.sendHPDU(this, file); } } catch (IOException e) { diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java b/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java index d5fe2d5..7f4b5dd 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java @@ -18,7 +18,6 @@ package com.android.ddmlib; import com.android.ddmlib.HeapSegment.HeapSegmentElement; -import java.io.File; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -304,10 +303,18 @@ public class ClientData { void onSuccess(String remoteFilePath, Client client); /** - * Called when the HPROF dump failed. - * @param client the client for which the HPROF file was. + * Called when a HPROF dump was successful. + * @param data the data containing the HPROF file, streamed from the VM + * @param client the client that was profiled. + */ + void onSuccess(byte[] data, Client client); + + /** + * Called when a hprof dump failed to end on the VM side + * @param client the client that was profiled. + * @param message an optional (<code>null<code> ok) error message to be displayed. */ - void onFailure(Client client); + void onEndFailure(Client client, String message); } /** @@ -323,10 +330,10 @@ public class ClientData { /** * Called when a method tracing was successful. - * @param remoteFilePath the device-side path of the trace file. + * @param data the data containing the trace file, streamed from the VM * @param client the client that was profiled. */ - void onSuccess(File localFile, Client client); + void onSuccess(byte[] data, Client client); /** * Called when method tracing failed to start @@ -341,13 +348,6 @@ public class ClientData { * @param message an optional (<code>null<code> ok) error message to be displayed. */ void onEndFailure(Client client, String message); - - /** - * Called when method tracing failed to end locally. - * @param client the client that was profiled. - * @param message the mandatory message to display. - */ - void onEndLocalFailure(Client client, String message); } /** diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java index f3f5497..e5b403b 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java @@ -272,7 +272,6 @@ final class HandleHeap extends ChunkHandler { finishChunkPacket(packet, CHUNK_HPDS, buf.position()); Log.d("ddm-heap", "Sending " + name(CHUNK_HPDS)); client.sendAndConsume(packet, mInst); - client.getClientData().setPendingHprofDump("[streaming]"); } /* @@ -296,7 +295,7 @@ final class HandleHeap extends ChunkHandler { Log.d("ddm-heap", "Heap dump request has finished"); } else { - handler.onFailure(client); + handler.onEndFailure(client, null); Log.w("ddm-heap", "Heap dump request failed (check device log)"); } } @@ -307,7 +306,15 @@ final class HandleHeap extends ChunkHandler { * hprof dump. */ private void handleHPDS(Client client, ByteBuffer data) { - Log.w("ddm-prof", "got hprof file, size: " + data.capacity() + " bytes"); + IHprofDumpHandler handler = ClientData.getHprofDumpHandler(); + if (handler != null) { + byte[] stuff = new byte[data.capacity()]; + data.get(stuff, 0, stuff.length); + + Log.d("ddm-hprof", "got hprof file, size: " + data.capacity() + " bytes"); + + handler.onSuccess(stuff, client); + } } /** diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java index 8526975..0595267 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java @@ -19,8 +19,6 @@ package com.android.ddmlib; import com.android.ddmlib.ClientData.IMethodProfilingHandler; import com.android.ddmlib.ClientData.MethodProfilingStatus; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -217,36 +215,12 @@ final class HandleProfiling extends ChunkHandler { private void handleMPSE(Client client, ByteBuffer data) { IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); if (handler != null) { - FileOutputStream fos = null; - try { - File f = File.createTempFile(client.getClientData().getClientDescription(), - ".trace"); - fos = new FileOutputStream(f); - - byte[] stuff = new byte[data.capacity()]; - data.get(stuff, 0, stuff.length); - - fos.write(stuff); - fos.close(); - fos = null; - - Log.d("ddm-prof", "got trace file, size: " + data.capacity() + " bytes"); - - handler.onSuccess(f, client); - } catch (IOException e) { - handler.onEndLocalFailure(client, e.getMessage()); - - Log.e("ddm-prof", "fail to write trace file: " + e.getMessage()); - Log.e("ddm-prof", e); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - //ignore - } - } - } + byte[] stuff = new byte[data.capacity()]; + data.get(stuff, 0, stuff.length); + + Log.d("ddm-prof", "got trace file, size: " + stuff.length + " bytes"); + + handler.onSuccess(stuff, client); } client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java index d6979cb..ed402c0 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java @@ -175,7 +175,9 @@ public class InfoPanel extends TablePanel { } item = mTable.getItem(ENT_SUPPORTS_HPROF); - if (cd.hasFeature(ClientData.FEATURE_HPROF)) { + if (cd.hasFeature(ClientData.FEATURE_HPROF_STREAMING)) { + item.setText(1, "Yes"); + } else if (cd.hasFeature(ClientData.FEATURE_HPROF)) { item.setText(1, "Yes (Application must be able to write on the SD Card)"); } else { item.setText(1, "No"); diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java index 3a2a2ef..6c086db 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java @@ -23,12 +23,17 @@ import com.android.ddmlib.SyncService.SyncResult; import com.android.ddmuilib.SyncProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Shell; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; /** @@ -37,7 +42,7 @@ import java.lang.reflect.InvocationTargetException; * @see IHprofDumpHandler * @see IMethodProfilingHandler */ -public class BaseFileHandler { +public abstract class BaseFileHandler { protected final Shell mParentShell; @@ -45,6 +50,8 @@ public class BaseFileHandler { mParentShell = parentShell; } + protected abstract String getDialogTitle(); + /** * Prompts the user for a save location and pulls the remote files into this location. * <p/>This <strong>must</strong> be called from the UI Thread. @@ -74,7 +81,39 @@ public class BaseFileHandler { } /** - * Pulls a file off of a device + * Prompts the user for a save location and copies a temp file into it. + * <p/>This <strong>must</strong> be called from the UI Thread. + * @param localFileName The default local name + * @param tempFilePath The name of the temp file to copy. + * @param title The title of the File Save dialog. + * @return true if success, false on error or cancel. + */ + protected boolean promptAndSave(String localFileName, byte[] data, String title) { + FileDialog fileDialog = new FileDialog(mParentShell, SWT.SAVE); + + fileDialog.setText(title); + fileDialog.setFileName(localFileName); + + String localFilePath = fileDialog.open(); + if (localFilePath != null) { + try { + saveFile(data, new File(localFilePath)); + return true; + } catch (IOException e) { + String errorMsg = e.getMessage(); + displayErrorInUiThread( + "Failed to save file '%1$s'%2$s", + localFilePath, + errorMsg != null ? ":\n" + errorMsg : "."); + } + } + + return false; + } + + /** + * Pulls a file off of a device. This displays a {@link ProgressMonitorDialog} and therefore + * must be run from the UI Thread. * @param sync the {@link SyncService} to use to pull the file. * @param localFilePath the path of the local file to create * @param remoteFilePath the path of the remote file to pull @@ -100,4 +139,61 @@ public class BaseFileHandler { return res[0]; } + + /** + * Display an error message. + * <p/>This will call about to {@link Display} to run this in an async {@link Runnable} in the + * UI Thread. This is safe to be called from a non-UI Thread. + * @param format the string to display + * @param args the string arguments + */ + protected void displayErrorInUiThread(final String format, final Object... args) { + mParentShell.getDisplay().asyncExec(new Runnable() { + public void run() { + MessageDialog.openError(mParentShell, getDialogTitle(), + String.format(format, args)); + } + }); + } + + /** + * Display an error message. + * This must be called from the UI Thread. + * @param format the string to display + * @param args the string arguments + */ + protected void displayErrorFromUiThread(final String format, final Object... args) { + MessageDialog.openError(mParentShell, getDialogTitle(), + String.format(format, args)); + } + + /** + * Saves a given data into a temp file and returns its corresponding {@link File} object. + * @param data the data to save + * @return the File into which the data was written or null if it failed. + * @throws IOException + */ + protected File saveTempFile(byte[] data) throws IOException { + File f = File.createTempFile("ddms", null); + saveFile(data, f); + return f; + } + + /** + * Saves some data into a given File. + * @param data the data to save + * @param output the file into the data is saved. + * @throws IOException + */ + protected void saveFile(byte[] data, File output) throws IOException { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(output); + fos.write(data); + } finally { + if (fos != null) { + fos.close(); + } + } + } } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java index 15cb907..b1d7f2a 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java @@ -25,7 +25,6 @@ import com.android.ddmlib.SyncService.SyncResult; import com.android.ddmuilib.DdmUiPreferences; import com.android.ddmuilib.console.DdmConsole; -import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Shell; import java.io.BufferedReader; @@ -45,46 +44,32 @@ public class MethodProfilingHandler extends BaseFileHandler super(parentShell); } - public void onStartFailure(final Client client, final String message) { - mParentShell.getDisplay().asyncExec(new Runnable() { - public void run() { - displayError( - "Unable to create Method Profiling file for application '%1$s'\n\n%2$s" + - "Check logcat for more information.", - client.getClientData().getClientDescription(), - message != null ? message + "\n\n" : ""); - } - }); + @Override + protected String getDialogTitle() { + return "Method Profiling Error"; } - public void onEndFailure(final Client client, final String message) { - mParentShell.getDisplay().asyncExec(new Runnable() { - public void run() { - displayError( - "Unable to finish Method Profiling for application '%1$s'\n\n%2$s" + - "Check logcat for more information.", - client.getClientData().getClientDescription(), - message != null ? message + "\n\n" : ""); - } - }); + public void onStartFailure(final Client client, final String message) { + displayErrorInUiThread( + "Unable to create Method Profiling file for application '%1$s'\n\n%2$s" + + "Check logcat for more information.", + client.getClientData().getClientDescription(), + message != null ? message + "\n\n" : ""); } - public void onEndLocalFailure(final Client client, final String message) { - mParentShell.getDisplay().asyncExec(new Runnable() { - public void run() { - displayError( - "Unable to write trace file locally for application '%1$s'\n\n%2$s", - client.getClientData().getClientDescription(), - message); - } - }); + public void onEndFailure(final Client client, final String message) { + displayErrorInUiThread( + "Unable to finish Method Profiling for application '%1$s'\n\n%2$s" + + "Check logcat for more information.", + client.getClientData().getClientDescription(), + message != null ? message + "\n\n" : ""); } public void onSuccess(final String remoteFilePath, final Client client) { mParentShell.getDisplay().asyncExec(new Runnable() { public void run() { if (remoteFilePath == null) { - displayError( + displayErrorFromUiThread( "Unable to download trace file: unknown file name.\n" + "This can happen if you disconnected the device while recording the trace."); return; @@ -97,11 +82,11 @@ public class MethodProfilingHandler extends BaseFileHandler if (sync != null) { pullAndOpen(sync, remoteFilePath); } else { - displayError("Unable to download trace file from device '%1$s'.", + displayErrorFromUiThread("Unable to download trace file from device '%1$s'.", device.getSerialNumber()); } } catch (Exception e) { - displayError("Unable to download trace file from device '%1$s'.", + displayErrorFromUiThread("Unable to download trace file from device '%1$s'.", device.getSerialNumber()); } } @@ -109,10 +94,21 @@ public class MethodProfilingHandler extends BaseFileHandler }); } - public void onSuccess(File localFile, final Client client) { - openInTraceview(localFile.getAbsolutePath()); + public void onSuccess(byte[] data, final Client client) { + try { + File tempFile = saveTempFile(data); + openInTraceview(tempFile.getAbsolutePath()); + } catch (IOException e) { + String errorMsg = e.getMessage(); + displayErrorInUiThread( + "Failed to save trace data into temp file%1$s", + errorMsg != null ? ":\n" + errorMsg : "."); + } } + /** + * pulls and open a file. This is run from the UI thread. + */ private void pullAndOpen(SyncService sync, String remoteFilePath) throws InvocationTargetException, InterruptedException, IOException { // get a temp file @@ -126,12 +122,12 @@ public class MethodProfilingHandler extends BaseFileHandler // open the temp file in traceview openInTraceview(tempPath); } else { - displayError("Unable to download trace file:\n\n%1$s", + displayErrorFromUiThread("Unable to download trace file:\n\n%1$s", result.getMessage()); } } else { // this really shouldn't happen. - displayError("Unable to download trace file."); + displayErrorFromUiThread("Unable to download trace file."); } } @@ -174,9 +170,4 @@ public class MethodProfilingHandler extends BaseFileHandler Log.e("traceview", e); } } - - private void displayError(String format, Object... args) { - MessageDialog.openError(mParentShell, "Method Profiling Error", - String.format(format, args)); - } } |