/*
* Command-line DEX optimization and verification entry point.
*
- * There are two ways to launch this:
+ * There are three ways to launch this:
* (1) From the VM. This takes a dozen args, one of which is a file
* descriptor that acts as both input and output. This allows us to
* remain ignorant of where the DEX data originally came from.
* a filename for debug messages. Many assumptions are made about
* what's going on (verification + optimization are enabled, boot
* class path is in BOOTCLASSPATH, etc).
+ * (3) On the host during a build for preoptimization. This behaves
+ * almost the same as (2), except it takes file names instead of
+ * file descriptors.
*
* There are some fragile aspects around bootclasspath entries, owing
* largely to the VM's history of working on whenever it thought it needed
#include "utils/Log.h"
#include "cutils/process_name.h"
+#include <fcntl.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <assert.h>
static const char* kClassesDex = "classes.dex";
* up front for the DEX optimization header.
*/
static int extractAndProcessZip(int zipFd, int cacheFd,
- const char* debugFileName, int isBootstrap, const char* bootClassPath,
+ const char* debugFileName, bool isBootstrap, const char* bootClassPath,
const char* dexoptFlagStr)
{
ZipArchive zippy;
goto bail;
}
- /*
- * Prep the VM and perform the optimization.
- */
+ /* Parse the options. */
+
DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */
+
if (dexoptFlagStr[0] != '\0') {
const char* opc;
const char* val;
if (opc != NULL) {
dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
}
+
+ opc = strstr(dexoptFlagStr, "u="); /* uniprocessor target */
+ if (opc != NULL) {
+ switch (*(opc+2)) {
+ case 'y': dexoptFlags |= DEXOPT_UNIPROCESSOR; break;
+ case 'n': dexoptFlags |= DEXOPT_SMP; break;
+ default: break;
+ }
+ }
}
+
+ /*
+ * Prep the VM and perform the optimization.
+ */
+
if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
dexoptFlags) != 0)
{
return result;
}
+/*
+ * Common functionality for normal device-side processing as well as
+ * preoptimization.
+ */
+static int processZipFile(int zipFd, int cacheFd, const char* zipName,
+ const char *dexoptFlags)
+{
+ int result = -1;
+ char* bcpCopy = NULL;
+
+ /*
+ * Check to see if this is a bootstrap class entry. If so, truncate
+ * the path.
+ */
+ const char* bcp = getenv("BOOTCLASSPATH");
+ if (bcp == NULL) {
+ LOGE("DexOptZ: BOOTCLASSPATH not set\n");
+ goto bail;
+ }
+
+ bool isBootstrap = false;
+ const char* match = strstr(bcp, zipName);
+ if (match != NULL) {
+ /*
+ * TODO: we have a partial string match, but that doesn't mean
+ * we've matched an entire path component. We should make sure
+ * that we're matching on the full zipName, and if not we
+ * should re-do the strstr starting at (match+1).
+ *
+ * The scenario would be a bootclasspath with something like
+ * "/system/framework/core.jar" while we're trying to optimize
+ * "/framework/core.jar". Not very likely since all paths are
+ * absolute and end with ".jar", but not impossible.
+ */
+ int matchOffset = match - bcp;
+ if (matchOffset > 0 && bcp[matchOffset-1] == ':')
+ matchOffset--;
+ LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
+ zipName, matchOffset);
+ bcpCopy = strdup(bcp);
+ bcpCopy[matchOffset] = '\0';
+
+ bcp = bcpCopy;
+ LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
+ isBootstrap = true;
+ }
+
+ result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
+ bcp, dexoptFlags);
+
+bail:
+ free(bcpCopy);
+ return result;
+}
/* advance to the next arg and extract it */
#define GET_ARG(_var, _func, _msg) \
* 1. "--zip"
* 2. zip fd (input, read-only)
* 3. cache fd (output, read-write, locked with flock)
- * 4. filename of file being optimized (used for debug messages and
- * for comparing against BOOTCLASSPATH -- does not need to be
+ * 4. filename of zipfile being optimized (used for debug messages and
+ * for comparing against BOOTCLASSPATH; does not need to be
* accessible or even exist)
* 5. dexopt flags
*
static int fromZip(int argc, char* const argv[])
{
int result = -1;
- int zipFd, cacheFd, vmBuildVersion;
- const char* inputFileName;
+ int zipFd, cacheFd;
+ const char* zipName;
char* bcpCopy = NULL;
- const char* dexoptFlagStr;
+ const char* dexoptFlags;
if (argc != 6) {
LOGE("Wrong number of args for --zip (found %d)\n", argc);
GET_ARG(zipFd, strtol, "bad zip fd");
GET_ARG(cacheFd, strtol, "bad cache fd");
- inputFileName = *++argv;
+ zipName = *++argv;
--argc;
- dexoptFlagStr = *++argv;
+ dexoptFlags = *++argv;
--argc;
- /*
- * Check to see if this is a bootstrap class entry. If so, truncate
- * the path.
- */
- const char* bcp = getenv("BOOTCLASSPATH");
- if (bcp == NULL) {
- LOGE("DexOptZ: BOOTCLASSPATH not set\n");
- goto bail;
- }
+ result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);
- int isBootstrap = false;
- const char* match = strstr(bcp, inputFileName);
- if (match != NULL) {
+bail:
+ return result;
+}
+
+/*
+ * Parse arguments for a preoptimization run. This is when dalvikvm is run
+ * on a host to optimize dex files for eventual running on a (different)
+ * device. We want:
+ * 0. (name of dexopt command -- ignored)
+ * 1. "--preopt"
+ * 2. zipfile name
+ * 3. output file name
+ * 4. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path. If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int preopt(int argc, char* const argv[])
+{
+ int zipFd = -1;
+ int outFd = -1;
+ int result = -1;
+
+ if (argc != 5) {
/*
- * TODO: we have a partial string match, but that doesn't mean
- * we've matched an entire path component. We should make sure
- * that we're matching on the full inputFileName, and if not we
- * should re-do the strstr starting at (match+1).
- *
- * The scenario would be a bootclasspath with something like
- * "/system/framework/core.jar" while we're trying to optimize
- * "/framework/core.jar". Not very likely since all paths are
- * absolute and end with ".jar", but not impossible.
+ * Use stderr here, since this variant is meant to be called on
+ * the host side.
*/
- int matchOffset = match - bcp;
- if (matchOffset > 0 && bcp[matchOffset-1] == ':')
- matchOffset--;
- LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
- inputFileName, matchOffset);
- bcpCopy = strdup(bcp);
- bcpCopy[matchOffset] = '\0';
+ fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",
+ argc);
+ goto bail;
+ }
- bcp = bcpCopy;
- LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
- isBootstrap = true;
+ const char* zipName = argv[2];
+ const char* outName = argv[3];
+ const char* dexoptFlags = argv[4];
+
+ if (strstr(dexoptFlags, "u=y") == NULL &&
+ strstr(dexoptFlags, "u=n") == NULL)
+ {
+ fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");
+ goto bail;
+ }
+
+ zipFd = open(zipName, O_RDONLY);
+ if (zipFd < 0) {
+ perror(argv[0]);
+ goto bail;
+ }
+
+ outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
+ if (outFd < 0) {
+ perror(argv[0]);
+ goto bail;
}
- result = extractAndProcessZip(zipFd, cacheFd, inputFileName,
- isBootstrap, bcp, dexoptFlagStr);
+ result = processZipFile(zipFd, outFd, zipName, dexoptFlags);
bail:
- free(bcpCopy);
+ if (zipFd >= 0) {
+ close(zipFd);
+ }
+
+ if (outFd >= 0) {
+ close(outFd);
+ }
+
return result;
}
bool onlyOptVerifiedDex = false;
DexClassVerifyMode verifyMode;
DexOptimizerMode dexOptMode;
- int dexoptFlags = 0;
/* ugh -- upgrade these to a bit field if they get any more complex */
if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
} else {
dexOptMode = OPTIMIZE_MODE_NONE;
}
- if ((flags & DEXOPT_GEN_REGISTER_MAP) != 0) {
- dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
- }
- if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
- dexoptFlags) != 0)
- {
+ if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
LOGE("VM init failed\n");
goto bail;
}
return fromZip(argc, argv);
else if (strcmp(argv[1], "--dex") == 0)
return fromDex(argc, argv);
+ else if (strcmp(argv[1], "--preopt") == 0)
+ return preopt(argc, argv);
}
- fprintf(stderr, "Usage: don't use this\n");
+ fprintf(stderr,
+ "Usage:\n\n"
+ "Short version: Don't use this.\n\n"
+ "Slightly longer version: This system-internal tool is used to\n"
+ "produce optimized dex files. See the source code for details.\n");
+
return 1;
}
-