Merge "Remove preload2 as it is not used"

This commit is contained in:
Treehugger Robot
2019-07-25 14:41:45 +00:00
committed by Gerrit Code Review
31 changed files with 0 additions and 3566 deletions

View File

@@ -1,50 +0,0 @@
// Copyright (C) 2015 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.
java_library_host {
name: "preload2",
srcs: ["src/**/*.java"],
// To connect to devices (and take hprof dumps).
static_libs: [
"ddmlib-prebuilt",
"tools-common-prebuilt",
// To process hprof dumps.
"perflib-prebuilt",
"trove-prebuilt",
"guavalib",
// For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
// convenience (and to not depend on internal JDK APIs).
"apache-harmony-jdwp-tests",
"junit",
],
// Copy to build artifacts
dist: {
targets: [
"dist_files",
],
},
}
// Copy the preload-tool shell script to the host's bin directory.
sh_binary_host {
name: "preload-tool",
src: "preload-tool",
required: ["preload2"],
}

View File

@@ -1,37 +0,0 @@
# Copyright (C) 2015 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.
# This script is used on the host only. It uses a common subset
# shell dialect that should work well. It is partially derived
# from art/tools/art.
function follow_links() {
if [ z"$BASH_SOURCE" != z ]; then
file="$BASH_SOURCE"
else
file="$0"
fi
while [ -h "$file" ]; do
# On Mac OS, readlink -f doesn't work.
file="$(readlink "$file")"
done
echo "$file"
}
PROG_NAME="$(follow_links)"
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
ANDROID_ROOT=$PROG_DIR/..
java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@

View File

@@ -1,224 +0,0 @@
/*
* Copyright (C) 2015 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.preload;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
/**
* Helper class for common communication with a Client (the ddms name for a running application).
*
* Instances take a default timeout parameter that's applied to all functions without explicit
* timeout. Timeouts are in milliseconds.
*/
public class ClientUtils {
private int defaultTimeout;
public ClientUtils() {
this(10000);
}
public ClientUtils(int defaultTimeout) {
this.defaultTimeout = defaultTimeout;
}
/**
* Shortcut for findClient with default timeout.
*/
public Client findClient(IDevice device, String processName, int processPid) {
return findClient(device, processName, processPid, defaultTimeout);
}
/**
* Find the client with the given process name or process id. The name takes precedence over
* the process id (if valid). Stop looking after the given timeout.
*
* @param device The device to communicate with.
* @param processName The name of the process. May be null.
* @param processPid The pid of the process. Values less than or equal to zero are ignored.
* @param timeout The amount of milliseconds to wait, at most.
* @return The client, if found. Otherwise null.
*/
public Client findClient(IDevice device, String processName, int processPid, int timeout) {
WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
return wfc.get();
}
/**
* Shortcut for findAllClients with default timeout.
*/
public Client[] findAllClients(IDevice device) {
return findAllClients(device, defaultTimeout);
}
/**
* Retrieve all clients known to the given device. Wait at most the given timeout.
*
* @param device The device to investigate.
* @param timeout The amount of milliseconds to wait, at most.
* @return An array of clients running on the given device. May be null depending on the
* device implementation.
*/
public Client[] findAllClients(IDevice device, int timeout) {
if (device.hasClients()) {
return device.getClients();
}
WaitForClients wfc = new WaitForClients(device, timeout);
return wfc.get();
}
private static class WaitForClient implements IClientChangeListener {
private IDevice device;
private String processName;
private int processPid;
private long timeout;
private Client result;
public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
this.device = device;
this.processName = processName;
this.processPid = processPid;
this.timeout = timeout;
this.result = null;
}
public Client get() {
synchronized (this) {
AndroidDebugBridge.addClientChangeListener(this);
// Maybe it's already there.
if (result == null) {
result = searchForClient(device);
}
if (result == null) {
try {
wait(timeout);
} catch (InterruptedException e) {
// Note: doesn't guard for spurious wakeup.
}
}
}
AndroidDebugBridge.removeClientChangeListener(this);
return result;
}
private Client searchForClient(IDevice device) {
if (processName != null) {
Client tmp = device.getClient(processName);
if (tmp != null) {
return tmp;
}
}
if (processPid > 0) {
String name = device.getClientName(processPid);
if (name != null && !name.isEmpty()) {
Client tmp = device.getClient(name);
if (tmp != null) {
return tmp;
}
}
}
if (processPid > 0) {
// Try manual search.
for (Client cl : device.getClients()) {
if (cl.getClientData().getPid() == processPid
&& cl.getClientData().getClientDescription() != null) {
return cl;
}
}
}
return null;
}
private boolean isTargetClient(Client c) {
if (processPid > 0 && c.getClientData().getPid() == processPid) {
return true;
}
if (processName != null
&& processName.equals(c.getClientData().getClientDescription())) {
return true;
}
return false;
}
@Override
public void clientChanged(Client arg0, int arg1) {
synchronized (this) {
if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
if (isTargetClient(arg0)) {
result = arg0;
notifyAll();
}
}
}
}
}
private static class WaitForClients implements IClientChangeListener {
private IDevice device;
private long timeout;
public WaitForClients(IDevice device, long timeout) {
this.device = device;
this.timeout = timeout;
}
public Client[] get() {
synchronized (this) {
AndroidDebugBridge.addClientChangeListener(this);
if (device.hasClients()) {
return device.getClients();
}
try {
wait(timeout); // Note: doesn't guard for spurious wakeup.
} catch (InterruptedException exc) {
}
// We will be woken up when the first client data arrives. Sleep a little longer
// to give (hopefully all of) the rest of the clients a chance to become available.
// Note: a loop with timeout is brittle as well and complicated, just accept this
// for now.
try {
Thread.sleep(500);
} catch (InterruptedException exc) {
}
}
AndroidDebugBridge.removeClientChangeListener(this);
return device.getClients();
}
@Override
public void clientChanged(Client arg0, int arg1) {
synchronized (this) {
if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
notifyAll();
}
}
}
}
}

View File

@@ -1,420 +0,0 @@
/*
* Copyright (C) 2015 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.preload;
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
import com.android.preload.classdataretrieval.hprof.Hprof;
import com.android.ddmlib.DdmPreferences;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.SyncException;
import com.android.ddmlib.TimeoutException;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* Helper class for some device routines.
*/
public class DeviceUtils {
// Locations
private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
// Shell commands
private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
private static final String START_SHELL_CMD = "start";
private static final String STOP_SHELL_CMD = "stop";
private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";
public static void init(int debugPort) {
DdmPreferences.setSelectedDebugPort(debugPort);
Hprof.init();
AndroidDebugBridge.init(true);
AndroidDebugBridge.createBridge();
}
/**
* Run a command in the shell on the device.
*/
public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
}
/**
* Run a command in the shell on the device. Collects and returns the console output.
*/
public static String doShellReturnString(IDevice device, String cmdline, long timeout,
TimeUnit unit) {
CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
doShell(device, cmdline, rec, timeout, unit);
return rec.toString();
}
/**
* Run a command in the shell on the device, directing all output to the given receiver.
*/
public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
long timeout, TimeUnit unit) {
try {
device.executeShellCommand(cmdline, receiver, timeout, unit);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Run am start on the device.
*/
public static void doAMStart(IDevice device, String name, String activity) {
doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
}
/**
* Find the device with the given serial. Give up after the given timeout (in milliseconds).
*/
public static IDevice findDevice(String serial, int timeout) {
WaitForDevice wfd = new WaitForDevice(serial, timeout);
return wfd.get();
}
/**
* Get all devices ddms knows about. Wait at most for the given timeout.
*/
public static IDevice[] findDevices(int timeout) {
WaitForDevice wfd = new WaitForDevice(null, timeout);
wfd.get();
return AndroidDebugBridge.getBridge().getDevices();
}
/**
* Return the build type of the given device. This is the value of the "ro.build.type"
* system property.
*/
public static String getBuildType(IDevice device) {
try {
Future<String> buildType = device.getSystemProperty("ro.build.type");
return buildType.get(500, TimeUnit.MILLISECONDS);
} catch (Exception e) {
}
return null;
}
/**
* Check whether the given device has a pre-optimized boot image. More precisely, checks
* whether /system/framework/ * /boot.art exists.
*/
public static boolean hasPrebuiltBootImage(IDevice device) {
String ret =
doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
return !ret.contains("No such file or directory");
}
/**
* Write over the preloaded-classes file with an empty or existing file and regenerate the boot
* image as necessary.
*
* @param device
* @param pcFile
* @param bootTimeout
* @throws AdbCommandRejectedException
* @throws IOException
* @throws TimeoutException
* @throws SyncException
* @return true if successfully overwritten, false otherwise
*/
public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
boolean writeEmpty = (pcFile == null);
if (writeEmpty) {
// Check if the preloaded-classes file is already empty.
String oldContent =
doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
if (oldContent.trim().equals("")) {
System.out.println("Preloaded-classes already empty.");
return true;
}
}
// Stop the system server etc.
doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
// Remount the read-only system partition
doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
// Delete the preloaded-classes file
doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
// Delete the dalvik cache files
doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
if (writeEmpty) {
// Write an empty preloaded-classes file
doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
} else {
// Push the new preloaded-classes file
device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
}
// Manually reset the boot complete flag
doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
// Restart system server on the device
doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
// Wait for the boot complete flag and return the outcome.
return waitForBootComplete(device, bootTimeout);
}
private static boolean waitForBootComplete(IDevice device, long timeout) {
// Do a loop checking each second whether bootcomplete. Wait for at most the given
// threshold.
Date startDate = new Date();
for (;;) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore spurious wakeup.
}
// Check whether bootcomplete.
String ret =
doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
if (ret.trim().equals("1")) {
break;
}
System.out.println("Still not booted: " + ret);
// Check whether we timed out. This is a simplistic check that doesn't take into account
// things like switches in time.
Date endDate = new Date();
long seconds =
TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
if (seconds > timeout) {
return false;
}
}
return true;
}
/**
* Enable method-tracing on device. The system should be restarted after this.
*/
public static void enableTracing(IDevice device) {
// Disable selinux.
doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
// Make the profile directory world-writable.
doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
// Enable streaming method tracing with a small 1K buffer.
doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace-file "
+ "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
}
private static class NullShellOutputReceiver implements IShellOutputReceiver {
@Override
public boolean isCancelled() {
return false;
}
@Override
public void flush() {}
@Override
public void addOutput(byte[] arg0, int arg1, int arg2) {}
}
private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
private StringBuilder builder = new StringBuilder();
@Override
public String toString() {
String ret = builder.toString();
// Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
while (ret.endsWith("\r") || ret.endsWith("\n")) {
ret = ret.substring(0, ret.length() - 1);
}
return ret;
}
@Override
public void addOutput(byte[] arg0, int arg1, int arg2) {
builder.append(new String(arg0, arg1, arg2));
}
@Override
public void flush() {}
@Override
public boolean isCancelled() {
return false;
}
}
private static class WaitForDevice {
private String serial;
private long timeout;
private IDevice device;
public WaitForDevice(String serial, long timeout) {
this.serial = serial;
this.timeout = timeout;
device = null;
}
public IDevice get() {
if (device == null) {
WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
synchronized (wfdl) {
AndroidDebugBridge.addDeviceChangeListener(wfdl);
// Check whether we already know about this device.
IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
if (serial != null) {
for (IDevice d : devices) {
if (serial.equals(d.getSerialNumber())) {
// Only accept if there are clients already. Else wait for the callback informing
// us that we now have clients.
if (d.hasClients()) {
device = d;
}
break;
}
}
} else {
if (devices.length > 0) {
device = devices[0];
}
}
if (device == null) {
try {
wfdl.wait(timeout);
} catch (InterruptedException e) {
// Ignore spurious wakeups.
}
device = wfdl.getDevice();
}
AndroidDebugBridge.removeDeviceChangeListener(wfdl);
}
}
if (device != null) {
// Wait for clients.
WaitForClientsListener wfcl = new WaitForClientsListener(device);
synchronized (wfcl) {
AndroidDebugBridge.addDeviceChangeListener(wfcl);
if (!device.hasClients()) {
try {
wfcl.wait(timeout);
} catch (InterruptedException e) {
// Ignore spurious wakeups.
}
}
AndroidDebugBridge.removeDeviceChangeListener(wfcl);
}
}
return device;
}
private static class WaitForDeviceListener implements IDeviceChangeListener {
private String serial;
private IDevice device;
public WaitForDeviceListener(String serial) {
this.serial = serial;
}
public IDevice getDevice() {
return device;
}
@Override
public void deviceChanged(IDevice arg0, int arg1) {
// We may get a device changed instead of connected. Handle like a connection.
deviceConnected(arg0);
}
@Override
public void deviceConnected(IDevice arg0) {
if (device != null) {
// Ignore updates.
return;
}
if (serial == null || serial.equals(arg0.getSerialNumber())) {
device = arg0;
synchronized (this) {
notifyAll();
}
}
}
@Override
public void deviceDisconnected(IDevice arg0) {
// Ignore disconnects.
}
}
private static class WaitForClientsListener implements IDeviceChangeListener {
private IDevice myDevice;
public WaitForClientsListener(IDevice myDevice) {
this.myDevice = myDevice;
}
@Override
public void deviceChanged(IDevice arg0, int arg1) {
if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
// Got a client list, done here.
synchronized (this) {
notifyAll();
}
}
}
@Override
public void deviceConnected(IDevice arg0) {
}
@Override
public void deviceDisconnected(IDevice arg0) {
}
}
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (C) 2015 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.preload;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Holds the collected data for a process.
*/
public class DumpData {
/**
* Name of the package (=application).
*/
String packageName;
/**
* A map of class name to a string for the classloader. This may be a toString equivalent,
* or just a unique ID.
*/
Map<String, String> dumpData;
/**
* The Date when this data was captured. Mostly for display purposes.
*/
Date date;
/**
* A cached value for the number of boot classpath classes (classloader value in dumpData is
* null).
*/
int bcpClasses;
public DumpData(String packageName, Map<String, String> dumpData, Date date) {
this.packageName = packageName;
this.dumpData = dumpData;
this.date = date;
countBootClassPath();
}
public String getPackageName() {
return packageName;
}
public Date getDate() {
return date;
}
public Map<String, String> getDumpData() {
return dumpData;
}
public void countBootClassPath() {
bcpClasses = 0;
for (Map.Entry<String, String> e : dumpData.entrySet()) {
if (e.getValue() == null) {
bcpClasses++;
}
}
}
// Return an inverted mapping.
public Map<String, Set<String>> invertData() {
Map<String, Set<String>> ret = new HashMap<>();
for (Map.Entry<String, String> e : dumpData.entrySet()) {
if (!ret.containsKey(e.getValue())) {
ret.put(e.getValue(), new HashSet<String>());
}
ret.get(e.getValue()).add(e.getKey());
}
return ret;
}
}

View File

@@ -1,141 +0,0 @@
/*
* Copyright (C) 2015 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.preload;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.io.FileReader;
import java.text.DateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* Helper class for serialization and deserialization of a collection of DumpData objects to XML.
*/
public class DumpDataIO {
/**
* Serialize the given collection to an XML document. Returns the produced string.
*/
public static String serialize(Collection<DumpData> data) {
// We'll do this by hand, constructing a DOM or similar is too complicated for our simple
// use case.
StringBuilder sb = new StringBuilder();
sb.append("<preloaded-classes-data>\n");
for (DumpData d : data) {
serialize(d, sb);
}
sb.append("</preloaded-classes-data>\n");
return sb.toString();
}
private static void serialize(DumpData d, StringBuilder sb) {
sb.append("<data package=\"" + d.packageName + "\" date=\"" +
DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
}
sb.append("</data>\n");
}
/**
* Load a collection of DumpData objects from the given file.
*/
public static Collection<DumpData> deserialize(File f) throws Exception {
// Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(false);
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
DumpDataContentHandler ddch = new DumpDataContentHandler();
xmlReader.setContentHandler(ddch);
xmlReader.parse(new InputSource(new FileReader(f)));
return ddch.data;
}
private static class DumpDataContentHandler extends DefaultHandler {
Collection<DumpData> data = new LinkedList<DumpData>();
DumpData openData = null;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if (qName.equals("data")) {
if (openData != null) {
throw new IllegalStateException();
}
String pkg = attributes.getValue("package");
String dateString = attributes.getValue("date");
if (pkg == null || dateString == null) {
throw new IllegalArgumentException();
}
try {
Date date = DateFormat.getDateTimeInstance().parse(dateString);
openData = new DumpData(pkg, new HashMap<String, String>(), date);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (qName.equals("class")) {
if (openData == null) {
throw new IllegalStateException();
}
String className = attributes.getValue("name");
String classLoader = attributes.getValue("classloader");
if (className == null || classLoader == null) {
throw new IllegalArgumentException();
}
openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("data")) {
if (openData == null) {
throw new IllegalStateException();
}
openData.countBootClassPath();
data.add(openData);
openData = null;
}
}
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright (C) 2015 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.preload;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
/**
* A table model for collected DumpData. This is both the internal storage as well as the model
* for display.
*/
public class DumpTableModel extends AbstractTableModel {
private List<DumpData> data = new ArrayList<DumpData>();
public void addData(DumpData d) {
data.add(d);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
public void clear() {
int size = data.size();
if (size > 0) {
data.clear();
fireTableRowsDeleted(0, size - 1);
}
}
public List<DumpData> getData() {
return data;
}
@Override
public int getRowCount() {
return data.size();
}
@Override
public int getColumnCount() {
return 4;
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return "Package";
case 1:
return "Date";
case 2:
return "# All Classes";
case 3:
return "# Boot Classpath Classes";
default:
throw new IndexOutOfBoundsException(String.valueOf(column));
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
DumpData d = data.get(rowIndex);
switch (columnIndex) {
case 0:
return d.packageName;
case 1:
return d.date;
case 2:
return d.dumpData.size();
case 3:
return d.bcpClasses;
default:
throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
}
}
}

View File

@@ -1,341 +0,0 @@
/*
* Copyright (C) 2015 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.preload;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
import com.android.preload.actions.ClearTableAction;
import com.android.preload.actions.ComputeThresholdAction;
import com.android.preload.actions.ComputeThresholdXAction;
import com.android.preload.actions.DeviceSpecific;
import com.android.preload.actions.ExportAction;
import com.android.preload.actions.ImportAction;
import com.android.preload.actions.ReloadListAction;
import com.android.preload.actions.RunMonkeyAction;
import com.android.preload.actions.ScanAllPackagesAction;
import com.android.preload.actions.ScanPackageAction;
import com.android.preload.actions.ShowDataAction;
import com.android.preload.actions.WritePreloadedClassesAction;
import com.android.preload.classdataretrieval.ClassDataRetriever;
import com.android.preload.classdataretrieval.hprof.Hprof;
import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
import com.android.preload.ui.IUI;
import com.android.preload.ui.SequenceUI;
import com.android.preload.ui.SwingUI;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.swing.Action;
import javax.swing.DefaultListModel;
public class Main {
/**
* Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
* off for now.
*/
public final static boolean ENABLE_TRACING = false;
/**
* Ten-second timeout.
*/
public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
/**
* Hprof timeout. Two minutes.
*/
public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
private IDevice device;
private static ClientUtils clientUtils;
private DumpTableModel dataTableModel;
private DefaultListModel<Client> clientListModel;
private IUI ui;
// Actions that need to be updated once a device is selected.
private Collection<DeviceSpecific> deviceSpecificActions;
// Current main instance.
private static Main top;
private static boolean useJdwpClassDataRetriever = false;
public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
+ "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
+ "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
// Threads
"android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
+ "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
+ "(.*\\$NoPreloadHolder$)";
public final static String SCAN_ALL_CMD = "scan-all";
public final static String SCAN_PACKAGE_CMD = "scan";
public final static String COMPUTE_FILE_CMD = "comp";
public final static String EXPORT_CMD = "export";
public final static String IMPORT_CMD = "import";
public final static String WRITE_CMD = "write";
/**
* @param args
*/
public static void main(String[] args) {
Main m;
if (args.length > 0 && args[0].equals("--seq")) {
m = createSequencedMain(args);
} else {
m = new Main(new SwingUI());
}
top = m;
m.startUp();
}
public Main(IUI ui) {
this.ui = ui;
clientListModel = new DefaultListModel<Client>();
dataTableModel = new DumpTableModel();
clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout.
List<Action> actions = new ArrayList<Action>();
actions.add(new ReloadListAction(clientUtils, null, clientListModel));
actions.add(new ClearTableAction(dataTableModel));
actions.add(new RunMonkeyAction(null, dataTableModel));
actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
CLASS_PRELOAD_BLACKLIST));
actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
null));
actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
CLASS_PRELOAD_BLACKLIST));
actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
actions.add(new ShowDataAction(dataTableModel));
actions.add(new ImportAction(dataTableModel));
actions.add(new ExportAction(dataTableModel));
deviceSpecificActions = new ArrayList<DeviceSpecific>();
for (Action a : actions) {
if (a instanceof DeviceSpecific) {
deviceSpecificActions.add((DeviceSpecific)a);
}
}
ui.prepare(clientListModel, dataTableModel, actions);
}
/**
* @param args
* @return
*/
private static Main createSequencedMain(String[] args) {
SequenceUI ui = new SequenceUI();
Main main = new Main(ui);
Iterator<String> it = Arrays.asList(args).iterator();
it.next(); // --seq
// Setup
ui.choice("#" + it.next()); // Device.
ui.confirmNo(); // Prepare: no.
// Actions
try {
while (it.hasNext()) {
String op = it.next();
// Operation: Scan a single package
if (SCAN_PACKAGE_CMD.equals(op)) {
System.out.println("Scanning package.");
ui.action(ScanPackageAction.class);
ui.client(it.next());
// Operation: Scan all packages
} else if (SCAN_ALL_CMD.equals(op)) {
System.out.println("Scanning all packages.");
ui.action(ScanAllPackagesAction.class);
// Operation: Export the output to a file
} else if (EXPORT_CMD.equals(op)) {
System.out.println("Exporting data.");
ui.action(ExportAction.class);
ui.output(new File(it.next()));
// Operation: Import the input from a file or directory
} else if (IMPORT_CMD.equals(op)) {
System.out.println("Importing data.");
File file = new File(it.next());
if (!file.exists()) {
throw new RuntimeException(
String.format("File does not exist, %s.", file.getAbsolutePath()));
} else if (file.isFile()) {
ui.action(ImportAction.class);
ui.input(file);
} else if (file.isDirectory()) {
for (File content : file.listFiles()) {
ui.action(ImportAction.class);
ui.input(content);
}
}
// Operation: Compute preloaded classes with specific threshold
} else if (COMPUTE_FILE_CMD.equals(op)) {
System.out.println("Compute preloaded classes.");
ui.action(ComputeThresholdXAction.class);
ui.input(it.next());
ui.confirmYes();
ui.output(new File(it.next()));
// Operation: Write preloaded classes from a specific file
} else if (WRITE_CMD.equals(op)) {
System.out.println("Writing preloaded classes.");
ui.action(WritePreloadedClassesAction.class);
ui.input(new File(it.next()));
}
}
} catch (NoSuchElementException e) {
System.out.println("Failed to parse action sequence correctly.");
throw e;
}
return main;
}
public static IUI getUI() {
return top.ui;
}
public static ClassDataRetriever getClassDataRetriever() {
if (useJdwpClassDataRetriever) {
return new JDWPClassDataRetriever();
} else {
return new Hprof(HPROF_TIMEOUT_MILLIS);
}
}
public IDevice getDevice() {
return device;
}
public void setDevice(IDevice device) {
this.device = device;
for (DeviceSpecific ds : deviceSpecificActions) {
ds.setDevice(device);
}
}
public DefaultListModel<Client> getClientListModel() {
return clientListModel;
}
static class DeviceWrapper {
IDevice device;
public DeviceWrapper(IDevice d) {
device = d;
}
@Override
public String toString() {
return device.getName() + " (#" + device.getSerialNumber() + ")";
}
}
private void startUp() {
getUI().showWaitDialog();
initDevice();
// Load clients.
new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
getUI().hideWaitDialog();
getUI().ready();
}
private void initDevice() {
DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
if (devices == null || devices.length == 0) {
throw new RuntimeException("Could not find any devices...");
}
getUI().hideWaitDialog();
DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
for (int i = 0; i < devices.length; i++) {
deviceWrappers[i] = new DeviceWrapper(devices[i]);
}
DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
deviceWrappers);
if (ret != null) {
setDevice(ret.device);
} else {
System.exit(0);
}
boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
"Do you want to prepare the device? This is highly recommended.");
if (prepare) {
String buildType = DeviceUtils.getBuildType(device);
if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
+ ")");
return;
}
if (DeviceUtils.hasPrebuiltBootImage(device)) {
Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
+ "image!");
return;
}
if (ENABLE_TRACING) {
DeviceUtils.enableTracing(device);
}
Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
+ "long time. Please be patient.");
boolean success = false;
try {
success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
} catch (Exception e) {
System.err.println(e);
} finally {
if (!success) {
Main.getUI().showMessageDialog(
"Removing preloaded-classes failed unexpectedly!");
}
}
}
}
public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
throws Exception {
Client client = clientUtils.findClient(device, packageName, -1);
if (client == null) {
throw new RuntimeException("Could not find client...");
}
System.out.println("Found client: " + client);
return getClassDataRetriever().getClassData(client);
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.Main;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
public abstract class AbstractThreadedAction extends AbstractAction implements Runnable {
protected AbstractThreadedAction(String title) {
super(title);
}
@Override
public void actionPerformed(ActionEvent e) {
if (Main.getUI().isSingleThreaded()) {
run();
} else {
new Thread(this).start();
}
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.IDevice;
import java.awt.event.ActionEvent;
public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction
implements DeviceSpecific {
protected IDevice device;
protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) {
super(title);
this.device = device;
}
@Override
public void setDevice(IDevice device) {
this.device = device;
}
@Override
public void actionPerformed(ActionEvent e) {
if (device == null) {
return;
}
super.actionPerformed(e);
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.DumpTableModel;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
public class ClearTableAction extends AbstractAction {
private final DumpTableModel dataTableModel;
public ClearTableAction(DumpTableModel dataTableModel) {
super("Clear");
this.dataTableModel = dataTableModel;
}
@Override
public void actionPerformed(ActionEvent e) {
dataTableModel.clear();
}
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
/**
* Compute an intersection of classes from the given data. A class is in the intersection if it
* appears in at least the number of threshold given packages. An optional blacklist can be
* used to filter classes from the intersection.
*/
public class ComputeThresholdAction extends AbstractThreadedAction {
protected int threshold;
private Pattern blacklist;
private DumpTableModel dataTableModel;
/**
* Create an action with the given parameters. The blacklist is a regular expression
* that filters classes.
*/
public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold,
String blacklist) {
super(name);
this.dataTableModel = dataTableModel;
this.threshold = threshold;
if (blacklist != null) {
this.blacklist = Pattern.compile(blacklist);
}
}
@Override
public void actionPerformed(ActionEvent e) {
List<DumpData> data = dataTableModel.getData();
if (data.size() == 0) {
Main.getUI().showMessageDialog("No data available, please scan packages or run "
+ "monkeys.");
return;
}
if (data.size() == 1) {
Main.getUI().showMessageDialog("Cannot compute list from only one data set, please "
+ "scan packages or run monkeys.");
return;
}
super.actionPerformed(e);
}
@Override
public void run() {
Main.getUI().showWaitDialog();
Map<String, Set<String>> uses = new HashMap<String, Set<String>>();
for (DumpData d : dataTableModel.getData()) {
Main.getUI().updateWaitDialog("Merging " + d.getPackageName());
updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData()));
}
Main.getUI().updateWaitDialog("Computing thresholded set");
Set<String> result = fromThreshold(uses, blacklist, threshold);
Main.getUI().hideWaitDialog();
boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
+ " classes, would you like to save to disk?", "Save?");
if (ret) {
File f = Main.getUI().showSaveDialog();
if (f != null) {
saveSet(result, f);
}
}
}
private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist,
int threshold) {
TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name.
for (Map.Entry<String, Set<String>> e : classUses.entrySet()) {
if (e.getValue().size() >= threshold) {
if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) {
ret.add(e.getKey());
}
}
}
return ret;
}
private static void updateClassUse(String pkg, Map<String, Set<String>> classUses,
Set<String> classes) {
for (String className : classes) {
Set<String> old = classUses.get(className);
if (old == null) {
classUses.put(className, new HashSet<String>());
}
classUses.get(className).add(pkg);
}
}
private static Set<String> getBootClassPathClasses(Map<String, String> source) {
Set<String> ret = new HashSet<>();
for (Map.Entry<String, String> e : source.entrySet()) {
if (e.getValue() == null) {
ret.add(e.getKey());
}
}
return ret;
}
private static void saveSet(Set<String> result, File f) {
try {
PrintWriter out = new PrintWriter(f);
for (String s : result) {
out.println(s);
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
public class ComputeThresholdXAction extends ComputeThresholdAction {
public ComputeThresholdXAction(String name, DumpTableModel dataTableModel,
String blacklist) {
super(name, dataTableModel, 1, blacklist);
}
@Override
public void run() {
String value = Main.getUI().showInputDialog("Threshold?");
if (value != null) {
try {
threshold = Integer.parseInt(value);
super.run();
} catch (Exception exc) {
}
}
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.IDevice;
/**
* Marks an action as being device-specific. The user must set the device through the specified
* method if the device selection changes.
*
* Implementors must tolerate a null device (for example, with a no-op). This includes calling
* any methods before setDevice has been called.
*/
public interface DeviceSpecific {
/**
* Set the device that should be used. Note that there is no restriction on calling other
* methods of the implementor before a setDevice call. Neither is device guaranteed to be
* non-null.
*
* @param device The device to use going forward.
*/
public void setDevice(IDevice device);
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.DumpDataIO;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.PrintWriter;
public class ExportAction extends AbstractThreadedAction {
private File lastSaveFile;
private DumpTableModel dataTableModel;
public ExportAction(DumpTableModel dataTableModel) {
super("Export data");
this.dataTableModel = dataTableModel;
}
@Override
public void actionPerformed(ActionEvent e) {
lastSaveFile = Main.getUI().showSaveDialog();
if (lastSaveFile != null) {
super.actionPerformed(e);
}
}
@Override
public void run() {
Main.getUI().showWaitDialog();
String serialized = DumpDataIO.serialize(dataTableModel.getData());
if (serialized != null) {
try {
PrintWriter out = new PrintWriter(lastSaveFile);
out.println(serialized);
out.close();
Main.getUI().hideWaitDialog();
} catch (Exception e) {
Main.getUI().hideWaitDialog();
Main.getUI().showMessageDialog("Failed writing: " + e.getMessage());
}
}
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.DumpData;
import com.android.preload.DumpDataIO;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.Collection;
import javax.swing.AbstractAction;
public class ImportAction extends AbstractThreadedAction {
private File[] lastOpenFiles;
private DumpTableModel dataTableModel;
public ImportAction(DumpTableModel dataTableModel) {
super("Import data");
this.dataTableModel = dataTableModel;
}
@Override
public void actionPerformed(ActionEvent e) {
lastOpenFiles = Main.getUI().showOpenDialog(true);
if (lastOpenFiles != null) {
super.actionPerformed(e);
}
}
@Override
public void run() {
Main.getUI().showWaitDialog();
try {
for (File f : lastOpenFiles) {
try {
Collection<DumpData> data = DumpDataIO.deserialize(f);
for (DumpData d : data) {
dataTableModel.addData(d);
}
} catch (Exception e) {
Main.getUI().showMessageDialog("Failed reading: " + e.getMessage());
}
}
} finally {
Main.getUI().hideWaitDialog();
}
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
import com.android.preload.ClientUtils;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.DefaultListModel;
public class ReloadListAction extends AbstractThreadedDeviceSpecificAction {
private ClientUtils clientUtils;
private final DefaultListModel<Client> clientListModel;
public ReloadListAction(ClientUtils utils, IDevice device,
DefaultListModel<Client> clientListModel) {
super("Reload", device);
this.clientUtils = utils;
this.clientListModel = clientListModel;
}
@Override
public void run() {
Client[] clients = clientUtils.findAllClients(device);
if (clients != null) {
Arrays.sort(clients, new ClientComparator());
}
clientListModel.removeAllElements();
for (Client c : clients) {
clientListModel.addElement(c);
}
}
private static class ClientComparator implements Comparator<Client> {
@Override
public int compare(Client o1, Client o2) {
String s1 = o1.getClientData().getClientDescription();
String s2 = o2.getClientData().getClientDescription();
if (s1 == null || s2 == null) {
// Not good, didn't get all data?
return (s1 == null) ? -1 : 1;
}
return s1.compareTo(s2);
}
}
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.IDevice;
import com.android.preload.DeviceUtils;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.awt.event.ActionEvent;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.swing.AbstractAction;
public class RunMonkeyAction extends AbstractAction implements DeviceSpecific {
private final static String DEFAULT_MONKEY_PACKAGES =
"com.android.calendar,com.android.gallery3d";
private IDevice device;
private DumpTableModel dataTableModel;
public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) {
super("Run monkey");
this.device = device;
this.dataTableModel = dataTableModel;
}
@Override
public void setDevice(IDevice device) {
this.device = device;
}
@Override
public void actionPerformed(ActionEvent e) {
String packages = Main.getUI().showInputDialog("Please enter packages name to run with"
+ " the monkey, or leave empty for default.");
if (packages == null) {
return;
}
if (packages.isEmpty()) {
packages = DEFAULT_MONKEY_PACKAGES;
}
Runnable r = new RunMonkeyRunnable(packages);
if (Main.getUI().isSingleThreaded()) {
r.run();
} else {
new Thread(r).start();
}
}
private class RunMonkeyRunnable implements Runnable {
private String packages;
private final static int ITERATIONS = 1000;
public RunMonkeyRunnable(String packages) {
this.packages = packages;
}
@Override
public void run() {
Main.getUI().showWaitDialog();
try {
String pkgs[] = packages.split(",");
for (String pkg : pkgs) {
Main.getUI().updateWaitDialog("Running monkey on " + pkg);
try {
// Stop running app.
forceStop(pkg);
// Little bit of breather here.
try {
Thread.sleep(1000);
} catch (Exception e) {
}
DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1,
TimeUnit.MINUTES);
Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
Map<String, String> data = Main.findAndGetClassData(device, pkg);
DumpData dumpData = new DumpData(pkg, data, new Date());
dataTableModel.addData(dumpData);
} catch (Exception e) {
e.printStackTrace();
} finally {
// Stop running app.
forceStop(pkg);
}
}
} finally {
Main.getUI().hideWaitDialog();
}
}
private void forceStop(String packageName) {
// Stop running app.
DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS);
DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS);
DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS);
}
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
import com.android.preload.ClientUtils;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.util.Date;
import java.util.Map;
public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction {
private ClientUtils clientUtils;
private DumpTableModel dataTableModel;
public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
super("Scan all packages", device);
this.clientUtils = utils;
this.dataTableModel = dataTableModel;
}
@Override
public void run() {
Main.getUI().showWaitDialog();
try {
Client[] clients = clientUtils.findAllClients(device);
for (Client c : clients) {
String pkg = c.getClientData().getClientDescription();
Main.getUI().showWaitDialog();
Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
try {
Map<String, String> data = Main.getClassDataRetriever().getClassData(c);
DumpData dumpData = new DumpData(pkg, data, new Date());
dataTableModel.addData(dumpData);
} catch (Exception e) {
e.printStackTrace();
}
}
} finally {
Main.getUI().hideWaitDialog();
}
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
import com.android.preload.ClientUtils;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.util.Date;
import java.util.Map;
public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction {
private ClientUtils clientUtils;
private DumpTableModel dataTableModel;
public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
super("Scan package", device);
this.clientUtils = utils;
this.dataTableModel = dataTableModel;
}
@Override
public void run() {
Main.getUI().showWaitDialog();
try {
Client client = Main.getUI().getSelectedClient();
if (client != null) {
work(client);
} else {
Client[] clients = clientUtils.findAllClients(device);
if (clients.length > 0) {
ClientWrapper[] clientWrappers = new ClientWrapper[clients.length];
for (int i = 0; i < clientWrappers.length; i++) {
clientWrappers[i] = new ClientWrapper(clients[i]);
}
Main.getUI().hideWaitDialog();
ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan",
"Choose package",
clientWrappers);
if (ret != null) {
work(ret.client);
}
}
}
} finally {
Main.getUI().hideWaitDialog();
}
}
private void work(Client c) {
String pkg = c.getClientData().getClientDescription();
Main.getUI().showWaitDialog();
Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
try {
Map<String, String> data = Main.findAndGetClassData(device, pkg);
DumpData dumpData = new DumpData(pkg, data, new Date());
dataTableModel.addData(dumpData);
} catch (Exception e) {
e.printStackTrace();
}
}
private static class ClientWrapper {
private Client client;
public ClientWrapper(Client c) {
client = c;
}
@Override
public String toString() {
return client.getClientData().getClientDescription() + " (pid "
+ client.getClientData().getPid() + ")";
}
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class ShowDataAction extends AbstractAction {
private DumpTableModel dataTableModel;
public ShowDataAction(DumpTableModel dataTableModel) {
super("Show data");
this.dataTableModel = dataTableModel;
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO(agampe): Auto-generated method stub
int selRow = Main.getUI().getSelectedDataTableRow();
if (selRow != -1) {
DumpData data = dataTableModel.getData().get(selRow);
Map<String, Set<String>> inv = data.invertData();
StringBuilder builder = new StringBuilder();
// First bootclasspath.
add(builder, "Boot classpath:", inv.get(null));
// Now everything else.
for (String k : inv.keySet()) {
if (k != null) {
builder.append("==================\n\n");
add(builder, k, inv.get(k));
}
}
JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate());
newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())),
BorderLayout.CENTER);
newFrame.setSize(800, 600);
newFrame.setLocationRelativeTo(null);
newFrame.setVisible(true);
}
}
private void add(StringBuilder builder, String head, Set<String> set) {
builder.append(head);
builder.append('\n');
addSet(builder, set);
builder.append('\n');
}
private void addSet(StringBuilder builder, Set<String> set) {
if (set == null) {
builder.append(" NONE\n");
return;
}
List<String> sorted = new ArrayList<>(set);
Collections.sort(sorted);
for (String s : sorted) {
builder.append(s);
builder.append('\n');
}
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright (C) 2015 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.preload.actions;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
import com.android.preload.ClientUtils;
import com.android.preload.DeviceUtils;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.Date;
import java.util.Map;
public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction {
private File preloadedClassFile;
public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
super("Write preloaded classes action", device);
}
@Override
public void actionPerformed(ActionEvent e) {
File[] files = Main.getUI().showOpenDialog(true);
if (files != null && files.length > 0) {
preloadedClassFile = files[0];
super.actionPerformed(e);
}
}
@Override
public void run() {
Main.getUI().showWaitDialog();
try {
// Write the new file with a 5-minute timeout
DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60);
} catch (Exception e) {
System.err.println(e);
} finally {
Main.getUI().hideWaitDialog();
}
}
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2015 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.preload.classdataretrieval;
import com.android.ddmlib.Client;
import java.util.Map;
/**
* Retrieve a class-to-classloader map for loaded classes from the client.
*/
public interface ClassDataRetriever {
public Map<String, String> getClassData(Client client);
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2015 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.preload.classdataretrieval.hprof;
import com.android.ddmlib.Client;
import com.android.ddmlib.ClientData.IHprofDumpHandler;
import java.util.ArrayList;
import java.util.List;
public class GeneralHprofDumpHandler implements IHprofDumpHandler {
private List<IHprofDumpHandler> handlers = new ArrayList<>();
public void addHandler(IHprofDumpHandler h) {
synchronized (handlers) {
handlers.add(h);
}
}
public void removeHandler(IHprofDumpHandler h) {
synchronized (handlers) {
handlers.remove(h);
}
}
private List<IHprofDumpHandler> getIterationList() {
synchronized (handlers) {
return new ArrayList<>(handlers);
}
}
@Override
public void onEndFailure(Client arg0, String arg1) {
List<IHprofDumpHandler> iterList = getIterationList();
for (IHprofDumpHandler h : iterList) {
h.onEndFailure(arg0, arg1);
}
}
@Override
public void onSuccess(String arg0, Client arg1) {
List<IHprofDumpHandler> iterList = getIterationList();
for (IHprofDumpHandler h : iterList) {
h.onSuccess(arg0, arg1);
}
}
@Override
public void onSuccess(byte[] arg0, Client arg1) {
List<IHprofDumpHandler> iterList = getIterationList();
for (IHprofDumpHandler h : iterList) {
h.onSuccess(arg0, arg1);
}
}
}

View File

@@ -1,228 +0,0 @@
/*
* Copyright (C) 2015 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.preload.classdataretrieval.hprof;
import com.android.ddmlib.Client;
import com.android.ddmlib.ClientData;
import com.android.ddmlib.ClientData.IHprofDumpHandler;
import com.android.preload.classdataretrieval.ClassDataRetriever;
import com.android.preload.ui.NullProgressMonitor;
import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Queries;
import com.android.tools.perflib.heap.Snapshot;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Hprof implements ClassDataRetriever {
private static GeneralHprofDumpHandler hprofHandler;
public static void init() {
synchronized(Hprof.class) {
if (hprofHandler == null) {
ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler());
}
}
}
public static File doHprof(Client client, int timeout) {
GetHprof gh = new GetHprof(client, timeout);
return gh.get();
}
/**
* Return a map of class names to class-loader names derived from the hprof dump.
*
* @param hprofLocalFile
*/
public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception {
Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile));
Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null);
Map<String, String> retValue = new HashMap<String, String>();
for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) {
for (ClassObj c : e.getValue()) {
String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString();
String cName = c.getClassName();
int aDepth = 0;
while (cName.endsWith("[]")) {
cName = cName.substring(0, cName.length()-2);
aDepth++;
}
String newName = transformPrimitiveClass(cName);
if (aDepth > 0) {
// Need to use kind-a descriptor syntax. If it was transformed, it is primitive.
if (newName.equals(cName)) {
newName = "L" + newName + ";";
}
for (int i = 0; i < aDepth; i++) {
newName = "[" + newName;
}
}
retValue.put(newName, cl);
}
}
// Free up memory.
snapshot.dispose();
return retValue;
}
private static Map<String, String> primitiveMapping;
static {
primitiveMapping = new HashMap<>();
primitiveMapping.put("boolean", "Z");
primitiveMapping.put("byte", "B");
primitiveMapping.put("char", "C");
primitiveMapping.put("double", "D");
primitiveMapping.put("float", "F");
primitiveMapping.put("int", "I");
primitiveMapping.put("long", "J");
primitiveMapping.put("short", "S");
primitiveMapping.put("void", "V");
}
private static String transformPrimitiveClass(String name) {
String rep = primitiveMapping.get(name);
if (rep != null) {
return rep;
}
return name;
}
private static class GetHprof implements IHprofDumpHandler {
private File target;
private long timeout;
private Client client;
public GetHprof(Client client, long timeout) {
this.client = client;
this.timeout = timeout;
}
public File get() {
synchronized (this) {
hprofHandler.addHandler(this);
client.dumpHprof();
if (target == null) {
try {
wait(timeout);
} catch (Exception e) {
System.out.println(e);
}
}
}
hprofHandler.removeHandler(this);
return target;
}
private void wakeUp() {
synchronized (this) {
notifyAll();
}
}
@Override
public void onEndFailure(Client arg0, String arg1) {
System.out.println("GetHprof.onEndFailure");
if (client == arg0) {
wakeUp();
}
}
private static File createTargetFile() {
try {
return File.createTempFile("ddms", ".hprof");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void onSuccess(String arg0, Client arg1) {
System.out.println("GetHprof.onSuccess");
if (client == arg1) {
try {
target = createTargetFile();
arg1.getDevice().getSyncService().pullFile(arg0,
target.getAbsoluteFile().toString(), new NullProgressMonitor());
} catch (Exception e) {
if (target != null) {
target.delete();
}
e.printStackTrace();
target = null;
}
wakeUp();
}
}
@Override
public void onSuccess(byte[] arg0, Client arg1) {
System.out.println("GetHprof.onSuccess");
if (client == arg1) {
try {
target = createTargetFile();
BufferedOutputStream out =
new BufferedOutputStream(new FileOutputStream(target));
out.write(arg0);
out.close();
} catch (Exception e) {
if (target != null) {
target.delete();
}
e.printStackTrace();
target = null;
}
wakeUp();
}
}
}
private int timeout;
public Hprof(int timeout) {
this.timeout = timeout;
}
@Override
public Map<String, String> getClassData(Client client) {
File hprofLocalFile = Hprof.doHprof(client, timeout);
if (hprofLocalFile == null) {
throw new RuntimeException("Failed getting dump...");
}
System.out.println("Dump file is " + hprofLocalFile);
try {
return analyzeHprof(hprofLocalFile);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
hprofLocalFile.delete();
}
}
}

View File

@@ -1,221 +0,0 @@
/*
* Copyright (C) 2015 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.preload.classdataretrieval.jdwp;
import com.android.ddmlib.Client;
import com.android.preload.classdataretrieval.ClassDataRetriever;
import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
import org.apache.harmony.jpda.tests.share.JPDALogWriter;
import org.apache.harmony.jpda.tests.share.JPDATestOptions;
import java.util.HashMap;
import java.util.Map;
public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
private final Client client;
public JDWPClassDataRetriever() {
this(null);
}
public JDWPClassDataRetriever(Client client) {
this.client = client;
}
@Override
protected String getDebuggeeClassName() {
return "<unset>";
}
@Override
public Map<String, String> getClassData(Client client) {
return new JDWPClassDataRetriever(client).retrieve();
}
private Map<String, String> retrieve() {
if (client == null) {
throw new IllegalStateException();
}
settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
settings.setDebuggeeSuspend("n");
logWriter = new JPDALogWriter(System.out, "", false);
try {
internalSetUp();
return retrieveImpl();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
internalTearDown();
}
}
private Map<String, String> retrieveImpl() {
try {
// Suspend the app.
{
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return null;
}
}
// List all classes.
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return null;
}
int classCount = reply.getNextValueAsInt();
System.out.println("Runtime reported " + classCount + " classes.");
Map<Long, String> classes = new HashMap<Long, String>();
Map<Long, String> arrayClasses = new HashMap<Long, String>();
for (int i = 0; i < classCount; i++) {
byte refTypeTag = reply.getNextValueAsByte();
long typeID = reply.getNextValueAsReferenceTypeID();
String signature = reply.getNextValueAsString();
/* int status = */ reply.getNextValueAsInt();
switch (refTypeTag) {
case JDWPConstants.TypeTag.CLASS:
case JDWPConstants.TypeTag.INTERFACE:
classes.put(typeID, signature);
break;
case JDWPConstants.TypeTag.ARRAY:
arrayClasses.put(typeID, signature);
break;
}
}
Map<String, String> result = new HashMap<String, String>();
// Parse all classes.
for (Map.Entry<Long, String> entry : classes.entrySet()) {
long typeID = entry.getKey();
String signature = entry.getValue();
if (!checkClass(typeID, signature, result)) {
System.err.println("Issue investigating " + signature);
}
}
// For arrays, look at the leaf component type.
for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
long typeID = entry.getKey();
String signature = entry.getValue();
if (!checkArrayClass(typeID, signature, result)) {
System.err.println("Issue investigating " + signature);
}
}
return result;
} finally {
// Resume the app.
{
CommandPacket packet = new CommandPacket(
JDWPCommands.VirtualMachineCommandSet.CommandSetID,
JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
/* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
}
}
}
private boolean checkClass(long typeID, String signature, Map<String, String> result) {
CommandPacket packet = new CommandPacket(
JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
packet.setNextValueAsReferenceTypeID(typeID);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return false;
}
long classLoaderID = reply.getNextValueAsObjectID();
// TODO: Investigate the classloader to have a better string?
String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
result.put(getClassName(signature), classLoaderString);
return true;
}
private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
// Classloaders of array classes are the same as the component class'.
CommandPacket packet = new CommandPacket(
JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
packet.setNextValueAsReferenceTypeID(typeID);
ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
return false;
}
long classLoaderID = reply.getNextValueAsObjectID();
// TODO: Investigate the classloader to have a better string?
String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
// For array classes, we *need* the signature directly.
result.put(signature, classLoaderString);
return true;
}
private static String getClassName(String signature) {
String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
return withoutLAndSemicolon.replace('/', '.');
}
private static JPDATestOptions createTestOptions(String address) {
JPDATestOptions options = new JPDATestOptions();
options.setAttachConnectorKind();
options.setTimeout(1000);
options.setWaitingTime(1000);
options.setTransportAddress(address);
return options;
}
@Override
protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
return new PreloadDebugeeWrapper(settings, logWriter);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) 2015 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.preload.classdataretrieval.jdwp;
import org.apache.harmony.jpda.tests.framework.LogWriter;
import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper;
import org.apache.harmony.jpda.tests.share.JPDATestOptions;
import java.io.IOException;
public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper {
public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) {
super(options, writer);
}
@Override
protected Process launchProcess(String cmdLine) throws IOException {
return null;
}
@Override
protected void WaitForProcessExit(Process process) {
}
}

View File

@@ -1,45 +0,0 @@
package com.android.preload.ui;
import com.android.ddmlib.Client;
import java.io.File;
import java.util.List;
import javax.swing.Action;
import javax.swing.ListModel;
import javax.swing.table.TableModel;
/**
* UI abstraction for the tool. This allows a graphical mode, command line mode,
* or silent mode.
*/
public interface IUI {
void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
List<Action> actions);
void ready();
boolean isSingleThreaded();
Client getSelectedClient();
int getSelectedDataTableRow();
void showWaitDialog();
void updateWaitDialog(String s);
void hideWaitDialog();
void showMessageDialog(String s);
boolean showConfirmDialog(String title, String message);
String showInputDialog(String message);
<T> T showChoiceDialog(String title, String message, T[] choices);
File showSaveDialog();
File[] showOpenDialog(boolean multi);
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2015 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.preload.ui;
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
public class NullProgressMonitor implements ISyncProgressMonitor {
@Override
public void advance(int arg0) {}
@Override
public boolean isCanceled() {
return false;
}
@Override
public void start(int arg0) {}
@Override
public void startSubTask(String arg0) {}
@Override
public void stop() {}
}

View File

@@ -1,222 +0,0 @@
package com.android.preload.ui;
import com.android.ddmlib.Client;
import com.android.ddmlib.ClientData;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import javax.swing.Action;
import javax.swing.ListModel;
import javax.swing.table.TableModel;
public class SequenceUI implements IUI {
private ListModel<Client> clientListModel;
@SuppressWarnings("unused")
private TableModel dataTableModel;
private List<Action> actions;
private List<Object> sequence = new LinkedList<>();
public SequenceUI() {
}
@Override
public boolean isSingleThreaded() {
return true;
}
@Override
public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
List<Action> actions) {
this.clientListModel = clientListModel;
this.dataTableModel = dataTableModel;
this.actions = actions;
}
public SequenceUI action(Action a) {
sequence.add(a);
return this;
}
public SequenceUI action(Class<? extends Action> actionClass) {
for (Action a : actions) {
if (actionClass.equals(a.getClass())) {
sequence.add(a);
return this;
}
}
throw new IllegalArgumentException("No action of class " + actionClass + " found.");
}
public SequenceUI confirmYes() {
sequence.add(Boolean.TRUE);
return this;
}
public SequenceUI confirmNo() {
sequence.add(Boolean.FALSE);
return this;
}
public SequenceUI input(String input) {
sequence.add(input);
return this;
}
public SequenceUI input(File... f) {
sequence.add(f);
return this;
}
public SequenceUI output(File f) {
sequence.add(f);
return this;
}
public SequenceUI tableRow(int i) {
sequence.add(i);
return this;
}
private class ClientSelector {
private String pkg;
public ClientSelector(String pkg) {
this.pkg = pkg;
}
public Client getClient() {
for (int i = 0; i < clientListModel.getSize(); i++) {
ClientData cd = clientListModel.getElementAt(i).getClientData();
if (cd != null) {
String s = cd.getClientDescription();
if (pkg.equals(s)) {
return clientListModel.getElementAt(i);
}
}
}
throw new RuntimeException("Didn't find client " + pkg);
}
}
public SequenceUI client(String pkg) {
sequence.add(new ClientSelector(pkg));
return this;
}
public SequenceUI choice(String pattern) {
sequence.add(pattern);
return this;
}
@Override
public void ready() {
// Run the actions.
// No iterator or foreach loop as the sequence will be emptied while running.
try {
while (!sequence.isEmpty()) {
Object next = sequence.remove(0);
if (next instanceof Action) {
((Action)next).actionPerformed(null);
} else {
throw new IllegalStateException("Didn't expect a non-action: " + next);
}
}
} catch (Exception e) {
e.printStackTrace(System.out);
}
// Now shut down.
System.exit(0);
}
@Override
public Client getSelectedClient() {
Object next = sequence.remove(0);
if (next instanceof ClientSelector) {
return ((ClientSelector)next).getClient();
}
throw new IllegalStateException("Unexpected: " + next);
}
@Override
public int getSelectedDataTableRow() {
Object next = sequence.remove(0);
if (next instanceof Integer) {
return ((Integer)next).intValue();
}
throw new IllegalStateException("Unexpected: " + next);
}
@Override
public void showWaitDialog() {
}
@Override
public void updateWaitDialog(String s) {
System.out.println(s);
}
@Override
public void hideWaitDialog() {
}
@Override
public void showMessageDialog(String s) {
System.out.println(s);
}
@Override
public boolean showConfirmDialog(String title, String message) {
Object next = sequence.remove(0);
if (next instanceof Boolean) {
return ((Boolean)next).booleanValue();
}
throw new IllegalStateException("Unexpected: " + next);
}
@Override
public String showInputDialog(String message) {
Object next = sequence.remove(0);
if (next instanceof String) {
return (String)next;
}
throw new IllegalStateException("Unexpected: " + next);
}
@Override
public <T> T showChoiceDialog(String title, String message, T[] choices) {
Object next = sequence.remove(0);
if (next instanceof String) {
String s = (String)next;
for (T t : choices) {
if (t.toString().contains(s)) {
return t;
}
}
return null;
}
throw new IllegalStateException("Unexpected: " + next);
}
@Override
public File showSaveDialog() {
Object next = sequence.remove(0);
if (next instanceof File) {
System.out.println(next);
return (File)next;
}
throw new IllegalStateException("Unexpected: " + next);
}
@Override
public File[] showOpenDialog(boolean multi) {
Object next = sequence.remove(0);
if (next instanceof File[]) {
return (File[])next;
}
throw new IllegalStateException("Unexpected: " + next);
}
}

View File

@@ -1,291 +0,0 @@
/*
* Copyright (C) 2015 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.preload.ui;
import com.android.ddmlib.Client;
import com.android.ddmlib.ClientData;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.io.File;
import java.util.List;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.table.TableModel;
public class SwingUI extends JFrame implements IUI {
private JList<Client> clientList;
private JTable dataTable;
// Shared file chooser, means the directory is retained.
private JFileChooser jfc;
public SwingUI() {
super("Preloaded-classes computation");
}
@Override
public boolean isSingleThreaded() {
return false;
}
@Override
public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
List<Action> actions) {
getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
BorderLayout.WEST);
clientList.setCellRenderer(new ClientListCellRenderer());
// clientList.addListSelectionListener(listener);
dataTable = new JTable(dataTableModel);
getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER);
JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
for (Action a : actions) {
if (a == null) {
toolbar.addSeparator();
} else {
toolbar.add(a);
}
}
getContentPane().add(toolbar, BorderLayout.PAGE_START);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 600);
setVisible(true);
}
@Override
public void ready() {
}
@Override
public Client getSelectedClient() {
return clientList.getSelectedValue();
}
@Override
public int getSelectedDataTableRow() {
return dataTable.getSelectedRow();
}
private JDialog currentWaitDialog = null;
@Override
public void showWaitDialog() {
if (currentWaitDialog == null) {
currentWaitDialog = new JDialog(this, "Please wait...", true);
currentWaitDialog.getContentPane().add(new JLabel("Please be patient."),
BorderLayout.CENTER);
JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL);
progress.setIndeterminate(true);
currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH);
currentWaitDialog.setSize(200, 100);
currentWaitDialog.setLocationRelativeTo(null);
showWaitDialogLater();
}
}
private void showWaitDialogLater() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(true); // This is blocking.
}
}
});
}
@Override
public void updateWaitDialog(String s) {
if (currentWaitDialog != null) {
((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
Dimension prefSize = currentWaitDialog.getPreferredSize();
Dimension curSize = currentWaitDialog.getSize();
if (prefSize.width > curSize.width || prefSize.height > curSize.height) {
currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width),
Math.max(prefSize.height, curSize.height));
currentWaitDialog.invalidate();
}
}
}
@Override
public void hideWaitDialog() {
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
currentWaitDialog = null;
}
}
@Override
public void showMessageDialog(String s) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
}
try {
JOptionPane.showMessageDialog(this, s);
} finally {
// And reshow it afterwards...
if (currentWaitDialog != null) {
showWaitDialogLater();
}
}
}
@Override
public boolean showConfirmDialog(String title, String message) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
}
try {
return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION)
== JOptionPane.YES_OPTION;
} finally {
// And reshow it afterwards...
if (currentWaitDialog != null) {
showWaitDialogLater();
}
}
}
@Override
public String showInputDialog(String message) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
}
try {
return JOptionPane.showInputDialog(message);
} finally {
// And reshow it afterwards...
if (currentWaitDialog != null) {
showWaitDialogLater();
}
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T showChoiceDialog(String title, String message, T[] choices) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
}
try{
return (T)JOptionPane.showInputDialog(this,
title,
message,
JOptionPane.QUESTION_MESSAGE,
null,
choices,
choices[0]);
} finally {
// And reshow it afterwards...
if (currentWaitDialog != null) {
showWaitDialogLater();
}
}
}
@Override
public File showSaveDialog() {
// Hide the wait dialog...
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
}
try{
if (jfc == null) {
jfc = new JFileChooser();
}
int ret = jfc.showSaveDialog(this);
if (ret == JFileChooser.APPROVE_OPTION) {
return jfc.getSelectedFile();
} else {
return null;
}
} finally {
// And reshow it afterwards...
if (currentWaitDialog != null) {
showWaitDialogLater();
}
}
}
@Override
public File[] showOpenDialog(boolean multi) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
}
try{
if (jfc == null) {
jfc = new JFileChooser();
}
jfc.setMultiSelectionEnabled(multi);
int ret = jfc.showOpenDialog(this);
if (ret == JFileChooser.APPROVE_OPTION) {
return jfc.getSelectedFiles();
} else {
return null;
}
} finally {
// And reshow it afterwards...
if (currentWaitDialog != null) {
showWaitDialogLater();
}
}
}
private class ClientListCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
ClientData cd = ((Client) value).getClientData();
String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")";
return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
}
}
}