OSDN Git Service

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