aboutsummaryrefslogtreecommitdiffstats
path: root/ddms
diff options
context:
space:
mode:
Diffstat (limited to 'ddms')
-rw-r--r--ddms/app/src/com/android/ddms/UIThread.java30
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Client.java6
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java26
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java13
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java38
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java4
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java100
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java75
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));
- }
}