Merge "Add functionality to overwrite the /etc/preloaded-classes file."
This commit is contained in:
committed by
Android (Google) Code Review
commit
9809b8fdd6
@@ -16,13 +16,18 @@
|
||||
|
||||
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;
|
||||
@@ -32,6 +37,18 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -119,43 +136,56 @@ public class DeviceUtils {
|
||||
return !ret.contains("No such file or directory");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove files involved in a standard build that interfere with collecting data. This will
|
||||
* remove /etc/preloaded-classes, which determines which classes are allocated already in the
|
||||
* boot image. It also deletes any compiled boot image on the device. Then it restarts the
|
||||
* device.
|
||||
*
|
||||
* This is a potentially long-running operation, as the boot after the deletion may take a while.
|
||||
* The method will abort after the given timeout.
|
||||
*/
|
||||
public static boolean removePreloaded(IDevice device, long preloadedWaitTimeInSeconds) {
|
||||
String oldContent =
|
||||
DeviceUtils.doShellReturnString(device, "cat /etc/preloaded-classes", 1, TimeUnit.SECONDS);
|
||||
if (oldContent.trim().equals("")) {
|
||||
System.out.println("Preloaded-classes already empty.");
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 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", 100, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Remount /system, delete /etc/preloaded-classes. It would be nice to use "adb remount,"
|
||||
// but AndroidDebugBridge doesn't expose it.
|
||||
doShell(device, "mount -o remount,rw /system", 500, TimeUnit.MILLISECONDS);
|
||||
doShell(device, "rm /etc/preloaded-classes", 100, TimeUnit.MILLISECONDS);
|
||||
// We do need an empty file.
|
||||
doShell(device, "touch /etc/preloaded-classes", 100, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Delete the files in the dalvik cache.
|
||||
doShell(device, "rm /data/dalvik-cache/*/*boot.art", 500, TimeUnit.MILLISECONDS);
|
||||
|
||||
// We'll try to use dev.bootcomplete to know when the system server is back up. But stop
|
||||
// doesn't reset it, so do it manually.
|
||||
doShell(device, "setprop dev.bootcomplete \"0\"", 500, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Start the system server.
|
||||
doShell(device, "start", 100, TimeUnit.MILLISECONDS);
|
||||
// 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();
|
||||
@@ -178,7 +208,7 @@ public class DeviceUtils {
|
||||
Date endDate = new Date();
|
||||
long seconds =
|
||||
TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
|
||||
if (seconds > preloadedWaitTimeInSeconds) {
|
||||
if (seconds > timeout) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ 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;
|
||||
@@ -96,6 +97,7 @@ public class Main {
|
||||
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
|
||||
@@ -132,6 +134,7 @@ public class Main {
|
||||
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));
|
||||
@@ -200,6 +203,11 @@ public class Main {
|
||||
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) {
|
||||
@@ -305,8 +313,16 @@ public class Main {
|
||||
|
||||
Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
|
||||
+ "long time. Please be patient.");
|
||||
if (!DeviceUtils.removePreloaded(device, 15 * 60) /* 15m timeout */) {
|
||||
Main.getUI().showMessageDialog("Removing preloaded-classes failed unexpectedly!");
|
||||
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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user