OSDN Git Service

resolved conflicts for merge of aa63a6a6 to gingerbread-plus-aosp
[android-x86/dalvik.git] / dexlist / DexList.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  * List all methods in all concrete classes in one or more DEX files.
19  */
20 #include "libdex/DexFile.h"
21 #include "libdex/DexClass.h"
22 #include "libdex/DexProto.h"
23 #include "libdex/SysUtil.h"
24 #include "libdex/CmdUtils.h"
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <stddef.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <getopt.h>
33 #include <errno.h>
34 #include <assert.h>
35
36 static const char* gProgName = "dexlist";
37
38 /* command-line args */
39 static struct {
40     char*       argCopy;
41     const char* classToFind;
42     const char* methodToFind;
43 } gParms;
44
45
46 /*
47  * Return a newly-allocated string for the "dot version" of the class
48  * name for the given type descriptor. That is, The initial "L" and
49  * final ";" (if any) have been removed and all occurrences of '/'
50  * have been changed to '.'.
51  */
52 static char* descriptorToDot(const char* str)
53 {
54     size_t at = strlen(str);
55     char* newStr;
56
57     if (str[0] == 'L') {
58         assert(str[at - 1] == ';');
59         at -= 2; /* Two fewer chars to copy. */
60         str++; /* Skip the 'L'. */
61     }
62
63     newStr = malloc(at + 1); /* Add one for the '\0'. */
64     newStr[at] = '\0';
65
66     while (at > 0) {
67         at--;
68         newStr[at] = (str[at] == '/') ? '.' : str[at];
69     }
70
71     return newStr;
72 }
73
74 /*
75  * Position table callback; we just want to catch the number of the
76  * first line in the method, which *should* correspond to the first
77  * entry from the table.  (Could also use "min" here.)
78  */
79 static int positionsCallback(void* cnxt, u4 address, u4 lineNum)
80 {
81     int* pFirstLine = (int*) cnxt;
82     if (*pFirstLine == -1)
83         *pFirstLine = lineNum;
84     return 0;
85 }
86
87
88 /*
89  * Dump a method.
90  */
91 void dumpMethod(DexFile* pDexFile, const char* fileName,
92     const DexMethod* pDexMethod, int i)
93 {
94     const DexMethodId* pMethodId;
95     const DexCode* pCode;
96     const char* classDescriptor;
97     const char* methodName;
98     int firstLine;
99
100     /* abstract and native methods don't get listed */
101     if (pDexMethod->codeOff == 0)
102         return;
103
104     pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
105     methodName = dexStringById(pDexFile, pMethodId->nameIdx);
106
107     classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
108
109     pCode = dexGetCode(pDexFile, pDexMethod);
110     assert(pCode != NULL);
111
112     /*
113      * If the filename is empty, then set it to something printable
114      * so that it is easier to parse.
115      *
116      * TODO: A method may override its class's default source file by
117      * specifying a different one in its debug info. This possibility
118      * should be handled here.
119      */
120     if (fileName == NULL || fileName[0] == 0) {
121         fileName = "(none)";
122     }
123
124     firstLine = -1;
125     dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
126         pDexMethod->accessFlags, positionsCallback, NULL, &firstLine);
127
128     char* className = descriptorToDot(classDescriptor);
129     char* desc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
130     u4 insnsOff = pDexMethod->codeOff + offsetof(DexCode, insns);
131
132     if (gParms.methodToFind != NULL &&
133         (strcmp(gParms.classToFind, className) != 0 ||
134          strcmp(gParms.methodToFind, methodName) != 0))
135     {
136         goto skip;
137     }
138
139     printf("0x%08x %d %s %s %s %s %d\n",
140         insnsOff, pCode->insnsSize * 2,
141         className, methodName, desc,
142         fileName, firstLine);
143
144 skip:
145     free(desc);
146     free(className);
147 }
148
149 /*
150  * Run through all direct and virtual methods in the class.
151  */
152 void dumpClass(DexFile* pDexFile, int idx)
153 {
154     const DexClassDef* pClassDef;
155     DexClassData* pClassData;
156     const u1* pEncodedData;
157     const char* fileName;
158     int i;
159
160     pClassDef = dexGetClassDef(pDexFile, idx);
161     pEncodedData = dexGetClassData(pDexFile, pClassDef);
162     pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
163
164     if (pClassData == NULL) {
165         fprintf(stderr, "Trouble reading class data\n");
166         return;
167     }
168
169     if (pClassDef->sourceFileIdx == 0xffffffff) {
170         fileName = NULL;
171     } else {
172         fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
173     }
174
175     /*
176      * TODO: Each class def points at a sourceFile, so maybe that
177      * should be printed out. However, this needs to be coordinated
178      * with the tools that parse this output.
179      */
180
181     for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
182         dumpMethod(pDexFile, fileName, &pClassData->directMethods[i], i);
183     }
184
185     for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
186         dumpMethod(pDexFile, fileName, &pClassData->virtualMethods[i], i);
187     }
188
189     free(pClassData);
190 }
191
192 /*
193  * Process a file.
194  *
195  * Returns 0 on success.
196  */
197 int process(const char* fileName)
198 {
199     DexFile* pDexFile = NULL;
200     MemMapping map;
201     bool mapped = false;
202     int result = -1;
203     UnzipToFileResult utfr;
204
205     utfr = dexOpenAndMap(fileName, NULL, &map, true);
206     if (utfr != kUTFRSuccess) {
207         if (utfr == kUTFRNoClassesDex) {
208             /* no classes.dex in the APK; pretend we succeeded */
209             result = 0;
210             goto bail;
211         }
212         fprintf(stderr, "Unable to process '%s'\n", fileName);
213         goto bail;
214     }
215     mapped = true;
216
217     pDexFile = dexFileParse(map.addr, map.length, kDexParseDefault);
218     if (pDexFile == NULL) {
219         fprintf(stderr, "Warning: DEX parse failed for '%s'\n", fileName);
220         goto bail;
221     }
222
223     printf("#%s\n", fileName);
224
225     int i;
226     for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
227         dumpClass(pDexFile, i);
228     }
229
230     result = 0;
231
232 bail:
233     if (mapped)
234         sysReleaseShmem(&map);
235     if (pDexFile != NULL)
236         dexFileFree(pDexFile);
237     return result;
238 }
239
240
241 /*
242  * Show usage.
243  */
244 void usage(void)
245 {
246     fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
247     fprintf(stderr, "%s: dexfile [dexfile2 ...]\n", gProgName);
248     fprintf(stderr, "\n");
249 }
250
251 /*
252  * Parse args.
253  */
254 int main(int argc, char* const argv[])
255 {
256     int result = 0;
257     int i;
258
259     /*
260      * Find all instances of the fully-qualified method name.  This isn't
261      * really what dexlist is for, but it's easy to do it here.
262      */
263     if (argc > 3 && strcmp(argv[1], "--method") == 0) {
264         gParms.argCopy = strdup(argv[2]);
265         char* meth = strrchr(gParms.argCopy, '.');
266         if (meth == NULL) {
267             fprintf(stderr, "Expected package.Class.method\n");
268             free(gParms.argCopy);
269             return 2;
270         }
271         *meth = '\0';
272         gParms.classToFind = gParms.argCopy;
273         gParms.methodToFind = meth+1;
274         argv += 2;
275         argc -= 2;
276     }
277
278     if (argc < 2) {
279         fprintf(stderr, "%s: no file specified\n", gProgName);
280         usage();
281         return 2;
282     }
283
284     /*
285      * Run through the list of files.  If one of them fails we contine on,
286      * only returning a failure at the end.
287      */
288     for (i = 1; i < argc; i++)
289         result |= process(argv[i]);
290
291     free(gParms.argCopy);
292     return result;
293 }