Loading tools/preload2/src/com/android/preload/DeviceUtils.java +66 −36 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading Loading @@ -120,42 +137,55 @@ public class DeviceUtils { } /** * 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. * Write over the preloaded-classes file with an empty or existing file and regenerate the boot * image as necessary. * * 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. * @param device * @param pcFile * @param bootTimeout * @throws AdbCommandRejectedException * @throws IOException * @throws TimeoutException * @throws SyncException * @return true if successfully overwritten, false otherwise */ public static boolean removePreloaded(IDevice device, long preloadedWaitTimeInSeconds) { 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 = DeviceUtils.doShellReturnString(device, "cat /etc/preloaded-classes", 1, TimeUnit.SECONDS); 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); 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(); Loading @@ -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; } } Loading tools/preload2/src/com/android/preload/Main.java +18 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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)); Loading Loading @@ -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) { Loading Loading @@ -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!"); } } } } Loading tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * 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(); } } } Loading
tools/preload2/src/com/android/preload/DeviceUtils.java +66 −36 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading Loading @@ -120,42 +137,55 @@ public class DeviceUtils { } /** * 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. * Write over the preloaded-classes file with an empty or existing file and regenerate the boot * image as necessary. * * 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. * @param device * @param pcFile * @param bootTimeout * @throws AdbCommandRejectedException * @throws IOException * @throws TimeoutException * @throws SyncException * @return true if successfully overwritten, false otherwise */ public static boolean removePreloaded(IDevice device, long preloadedWaitTimeInSeconds) { 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 = DeviceUtils.doShellReturnString(device, "cat /etc/preloaded-classes", 1, TimeUnit.SECONDS); 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); 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(); Loading @@ -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; } } Loading
tools/preload2/src/com/android/preload/Main.java +18 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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)); Loading Loading @@ -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) { Loading Loading @@ -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!"); } } } } Loading
tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * 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(); } } }