OSDN Git Service

Reactivating tests disabled because of ClassLoader loop
[android-x86/dalvik.git] / dexopt / OptMain.c
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * Command-line DEX optimization and verification entry point.
19  *
20  * There are two ways to launch this:
21  * (1) From the VM.  This takes a dozen args, one of which is a file
22  *     descriptor that acts as both input and output.  This allows us to
23  *     remain ignorant of where the DEX data originally came from.
24  * (2) From installd or another native application.  Pass in a file
25  *     descriptor for a zip file, a file descriptor for the output, and
26  *     a filename for debug messages.  Many assumptions are made about
27  *     what's going on (verification + optimization are enabled, boot
28  *     class path is in BOOTCLASSPATH, etc).
29  *
30  * There are some fragile aspects around bootclasspath entries, owing
31  * largely to the VM's history of working on whenever it thought it needed
32  * instead of strictly doing what it was told.  If optimizing bootclasspath
33  * entries, always do them in the order in which they appear in the path.
34  */
35 #include "Dalvik.h"
36 #include "libdex/OptInvocation.h"
37
38 #include "utils/Log.h"
39 #include "cutils/process_name.h"
40
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <assert.h>
45
46 static const char* kClassesDex = "classes.dex";
47
48
49 /*
50  * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
51  * up front for the DEX optimization header.
52  */
53 static int extractAndProcessZip(int zipFd, int cacheFd,
54     const char* debugFileName, int isBootstrap, const char* bootClassPath,
55     const char* dexoptFlagStr)
56 {
57     ZipArchive zippy;
58     ZipEntry zipEntry;
59     long uncompLen, modWhen, crc32;
60     off_t dexOffset;
61     int err;
62     int result = -1;
63
64     memset(&zippy, 0, sizeof(zippy));
65
66     /* make sure we're still at the start of an empty file */
67     if (lseek(cacheFd, 0, SEEK_END) != 0) {
68         LOGE("DexOptZ: new cache file '%s' is not empty\n", debugFileName);
69         goto bail;
70     }
71
72     /*
73      * Write a skeletal DEX optimization header.  We want the classes.dex
74      * to come just after it.
75      */
76     err = dexOptCreateEmptyHeader(cacheFd);
77     if (err != 0)
78         goto bail;
79
80     /* record the file position so we can get back here later */
81     dexOffset = lseek(cacheFd, 0, SEEK_CUR);
82     if (dexOffset < 0)
83         goto bail;
84
85     /*
86      * Open the zip archive, find the DEX entry.
87      */
88     if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
89         LOGW("DexOptZ: unable to open zip archive '%s'\n", debugFileName);
90         goto bail;
91     }
92
93     zipEntry = dexZipFindEntry(&zippy, kClassesDex);
94     if (zipEntry == NULL) {
95         LOGW("DexOptZ: zip archive '%s' does not include %s\n",
96             debugFileName, kClassesDex);
97         goto bail;
98     }
99
100     /*
101      * Extract some info about the zip entry.
102      */
103     if (!dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
104             &modWhen, &crc32))
105     {
106         LOGW("DexOptZ: zip archive GetEntryInfo failed on %s\n", debugFileName);
107         goto bail;
108     }
109
110     uncompLen = uncompLen;
111     modWhen = modWhen;
112     crc32 = crc32;
113
114     /*
115      * Extract the DEX data into the cache file at the current offset.
116      */
117     if (!dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd)) {
118         LOGW("DexOptZ: extraction of %s from %s failed\n",
119             kClassesDex, debugFileName);
120         goto bail;
121     }
122
123     /*
124      * Prep the VM and perform the optimization.
125      */
126     DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
127     DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
128     int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */
129     if (dexoptFlagStr[0] != '\0') {
130         const char* opc;
131         const char* val;
132
133         opc = strstr(dexoptFlagStr, "v=");      /* verification */
134         if (opc != NULL) {
135             switch (*(opc+2)) {
136             case 'n':   verifyMode = VERIFY_MODE_NONE;          break;
137             case 'r':   verifyMode = VERIFY_MODE_REMOTE;        break;
138             case 'a':   verifyMode = VERIFY_MODE_ALL;           break;
139             default:                                            break;
140             }
141         }
142
143         opc = strstr(dexoptFlagStr, "o=");      /* optimization */
144         if (opc != NULL) {
145             switch (*(opc+2)) {
146             case 'n':   dexOptMode = OPTIMIZE_MODE_NONE;        break;
147             case 'v':   dexOptMode = OPTIMIZE_MODE_VERIFIED;    break;
148             case 'a':   dexOptMode = OPTIMIZE_MODE_ALL;         break;
149             default:                                            break;
150             }
151         }
152
153         opc = strstr(dexoptFlagStr, "m=y");     /* register map */
154         if (opc != NULL) {
155             dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
156         }
157     }
158     if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
159             dexoptFlags) != 0)
160     {
161         LOGE("DexOptZ: VM init failed\n");
162         goto bail;
163     }
164
165     //vmStarted = 1;
166
167     /* do the optimization */
168     if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
169             modWhen, crc32, isBootstrap))
170     {
171         LOGE("Optimization failed\n");
172         goto bail;
173     }
174
175     /* we don't shut the VM down -- process is about to exit */
176
177     result = 0;
178
179 bail:
180     dexZipCloseArchive(&zippy);
181     return result;
182 }
183
184
185 /* advance to the next arg and extract it */
186 #define GET_ARG(_var, _func, _msg)                                          \
187     {                                                                       \
188         char* endp;                                                         \
189         (_var) = _func(*++argv, &endp, 0);                                  \
190         if (*endp != '\0') {                                                \
191             LOGE("%s '%s'", _msg, *argv);                                   \
192             goto bail;                                                      \
193         }                                                                   \
194         --argc;                                                             \
195     }
196
197 /*
198  * Parse arguments.  We want:
199  *   0. (name of dexopt command -- ignored)
200  *   1. "--zip"
201  *   2. zip fd (input, read-only)
202  *   3. cache fd (output, read-write, locked with flock)
203  *   4. filename of file being optimized (used for debug messages and
204  *      for comparing against BOOTCLASSPATH -- does not need to be
205  *      accessible or even exist)
206  *   5. dexopt flags
207  *
208  * The BOOTCLASSPATH environment variable is assumed to hold the correct
209  * boot class path.  If the filename provided appears in the boot class
210  * path, the path will be truncated just before that entry (so that, if
211  * you were to dexopt "core.jar", your bootclasspath would be empty).
212  *
213  * This does not try to normalize the boot class path name, so the
214  * filename test won't catch you if you get creative.
215  */
216 static int fromZip(int argc, char* const argv[])
217 {
218     int result = -1;
219     int zipFd, cacheFd, vmBuildVersion;
220     const char* inputFileName;
221     char* bcpCopy = NULL;
222     const char* dexoptFlagStr;
223
224     if (argc != 6) {
225         LOGE("Wrong number of args for --zip (found %d)\n", argc);
226         goto bail;
227     }
228
229     /* skip "--zip" */
230     argc--;
231     argv++;
232
233     GET_ARG(zipFd, strtol, "bad zip fd");
234     GET_ARG(cacheFd, strtol, "bad cache fd");
235     inputFileName = *++argv;
236     --argc;
237     dexoptFlagStr = *++argv;
238     --argc;
239
240     /*
241      * Check to see if this is a bootstrap class entry.  If so, truncate
242      * the path.
243      */
244     const char* bcp = getenv("BOOTCLASSPATH");
245     if (bcp == NULL) {
246         LOGE("DexOptZ: BOOTCLASSPATH not set\n");
247         goto bail;
248     }
249
250     int isBootstrap = false;
251     const char* match = strstr(bcp, inputFileName);
252     if (match != NULL) {
253         /*
254          * TODO: we have a partial string match, but that doesn't mean
255          * we've matched an entire path component.  We should make sure
256          * that we're matching on the full inputFileName, and if not we
257          * should re-do the strstr starting at (match+1).
258          *
259          * The scenario would be a bootclasspath with something like
260          * "/system/framework/core.jar" while we're trying to optimize
261          * "/framework/core.jar".  Not very likely since all paths are
262          * absolute and end with ".jar", but not impossible.
263          */
264         int matchOffset = match - bcp;
265         if (matchOffset > 0 && bcp[matchOffset-1] == ':')
266             matchOffset--;
267         LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
268             inputFileName, matchOffset);
269         bcpCopy = strdup(bcp);
270         bcpCopy[matchOffset] = '\0';
271
272         bcp = bcpCopy;
273         LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
274         isBootstrap = true;
275     }
276
277     result = extractAndProcessZip(zipFd, cacheFd, inputFileName,
278                 isBootstrap, bcp, dexoptFlagStr);
279
280 bail:
281     free(bcpCopy);
282     return result;
283 }
284
285 /*
286  * Parse arguments for an "old-style" invocation directly from the VM.
287  *
288  * Here's what we want:
289  *   0. (name of dexopt command -- ignored)
290  *   1. "--dex"
291  *   2. DALVIK_VM_BUILD value, as a sanity check
292  *   3. file descriptor, locked with flock, for DEX file being optimized
293  *   4. DEX offset within file
294  *   5. DEX length
295  *   6. filename of file being optimized (for debug messages only)
296  *   7. modification date of source (goes into dependency section)
297  *   8. CRC of source (goes into dependency section)
298  *   9. flags (optimization level, isBootstrap)
299  *  10. bootclasspath entry #1
300  *  11. bootclasspath entry #2
301  *   ...
302  *
303  * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
304  * argument list and calls this executable.
305  *
306  * The bootclasspath entries become the dependencies for this DEX file.
307  *
308  * The open file descriptor MUST NOT be for one of the bootclasspath files.
309  * The parent has the descriptor locked, and we'll try to lock it again as
310  * part of processing the bootclasspath.  (We can catch this and return
311  * an error by comparing filenames or by opening the bootclasspath files
312  * and stat()ing them for inode numbers).
313  */
314 static int fromDex(int argc, char* const argv[])
315 {
316     int result = -1;
317     bool vmStarted = false;
318     char* bootClassPath = NULL;
319     int fd, flags, vmBuildVersion;
320     long offset, length;
321     const char* debugFileName;
322     u4 crc, modWhen;
323     char* endp;
324
325     if (argc < 10) {
326         /* don't have all mandatory args */
327         LOGE("Not enough arguments for --dex (found %d)\n", argc);
328         goto bail;
329     }
330
331     /* skip "--dex" */
332     argc--;
333     argv++;
334
335     /*
336      * Extract the args.
337      */
338     GET_ARG(vmBuildVersion, strtol, "bad vm build");
339     if (vmBuildVersion != DALVIK_VM_BUILD) {
340         LOGE("Inconsistent build rev: %d vs %d\n",
341             vmBuildVersion, DALVIK_VM_BUILD);
342         goto bail;
343     }
344     GET_ARG(fd, strtol, "bad fd");
345     GET_ARG(offset, strtol, "bad offset");
346     GET_ARG(length, strtol, "bad length");
347     debugFileName = *++argv;
348     --argc;
349     GET_ARG(modWhen, strtoul, "bad modWhen");
350     GET_ARG(crc, strtoul, "bad crc");
351     GET_ARG(flags, strtol, "bad flags");
352
353     LOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=0x%x crc=0x%x flg=%d (argc=%d)\n",
354         fd, offset, length, debugFileName, modWhen, crc, flags, argc);
355     assert(argc > 0);
356
357     if (--argc == 0) {
358         bootClassPath = strdup("");
359     } else {
360         int i, bcpLen;
361         char* const* argp;
362         char* cp;
363
364         bcpLen = 0;
365         for (i = 0, argp = argv; i < argc; i++) {
366             ++argp;
367             LOGV("DEP: '%s'\n", *argp);
368             bcpLen += strlen(*argp) + 1;
369         }
370
371         cp = bootClassPath = (char*) malloc(bcpLen +1);
372         for (i = 0, argp = argv; i < argc; i++) {
373             int strLen;
374
375             ++argp;
376             strLen = strlen(*argp);
377             if (i != 0)
378                 *cp++ = ':';
379             memcpy(cp, *argp, strLen);
380             cp += strLen;
381         }
382         *cp = '\0';
383
384         assert((int) strlen(bootClassPath) == bcpLen-1);
385     }
386     LOGV("  bootclasspath is '%s'\n", bootClassPath);
387
388     /* start the VM partway */
389     bool onlyOptVerifiedDex = false;
390     DexClassVerifyMode verifyMode;
391     DexOptimizerMode dexOptMode;
392     int dexoptFlags = 0;
393
394     /* ugh -- upgrade these to a bit field if they get any more complex */
395     if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
396         if ((flags & DEXOPT_VERIFY_ALL) != 0)
397             verifyMode = VERIFY_MODE_ALL;
398         else
399             verifyMode = VERIFY_MODE_REMOTE;
400     } else {
401         verifyMode = VERIFY_MODE_NONE;
402     }
403     if ((flags & DEXOPT_OPT_ENABLED) != 0) {
404         if ((flags & DEXOPT_OPT_ALL) != 0)
405             dexOptMode = OPTIMIZE_MODE_ALL;
406         else
407             dexOptMode = OPTIMIZE_MODE_VERIFIED;
408     } else {
409         dexOptMode = OPTIMIZE_MODE_NONE;
410     }
411     if ((flags & DEXOPT_GEN_REGISTER_MAP) != 0) {
412         dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
413     }
414
415     if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
416             dexoptFlags) != 0)
417     {
418         LOGE("VM init failed\n");
419         goto bail;
420     }
421
422     vmStarted = true;
423
424     /* do the optimization */
425     if (!dvmContinueOptimization(fd, offset, length, debugFileName,
426             modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
427     {
428         LOGE("Optimization failed\n");
429         goto bail;
430     }
431
432     result = 0;
433
434 bail:
435     /*
436      * In theory we should gracefully shut the VM down at this point.  In
437      * practice that only matters if we're checking for memory leaks with
438      * valgrind -- simply exiting is much faster.
439      *
440      * As it turns out, the DEX optimizer plays a little fast and loose
441      * with class loading.  We load all of the classes from a partially-
442      * formed DEX file, which is unmapped when we're done.  If we want to
443      * do clean shutdown here, perhaps for testing with valgrind, we need
444      * to skip the munmap call there.
445      */
446 #if 0
447     if (vmStarted) {
448         LOGI("DexOpt shutting down, result=%d\n", result);
449         dvmShutdown();
450     }
451 #endif
452
453     //dvmLinearAllocDump(NULL);
454
455 #if 0
456     {
457         extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData,
458                gDvm__gcSimpleData;
459         LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n",
460             gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData);
461     }
462 #endif
463
464     free(bootClassPath);
465     LOGV("DexOpt command complete (result=%d)\n", result);
466     return result;
467 }
468
469 /*
470  * Main entry point.  Decide where to go.
471  */
472 int main(int argc, char* const argv[])
473 {
474     set_process_name("dexopt");
475
476     setvbuf(stdout, NULL, _IONBF, 0);
477
478     if (argc > 1) {
479         if (strcmp(argv[1], "--zip") == 0)
480             return fromZip(argc, argv);
481         else if (strcmp(argv[1], "--dex") == 0)
482             return fromDex(argc, argv);
483     }
484
485     fprintf(stderr, "Usage: don't use this\n");
486     return 1;
487 }
488