Codebase list android-platform-dalvik / 6c110fd
New upstream version 7.0.0+r1 Kai-Chung Yan (殷啟聰) 7 years ago
38 changed file(s) with 435 addition(s) and 3956 deletion(s). Raw diff Collapse all Expand all
1616 subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
1717 libdex \
1818 dexgen \
19 dexlist \
2019 dexdump \
2120 dx \
2221 tools \
4141 LOCAL_C_INCLUDES := $(dexdump_c_includes)
4242 LOCAL_SHARED_LIBRARIES := libz liblog libutils
4343 LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries_sdk)
44 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
45 LOCAL_MODULE_TAGS := optional
4644 LOCAL_LDLIBS +=
4745 LOCAL_32_BIT_ONLY := true
48 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
4946 include $(BUILD_EXECUTABLE)
5047
5148 endif # !SDK_ONLY
5855 ##
5956 include $(CLEAR_VARS)
6057 LOCAL_MODULE := dexdump
61 LOCAL_MODULE_TAGS := optional
58 LOCAL_MODULE_HOST_OS := darwin linux windows
6259 LOCAL_SRC_FILES := $(dexdump_src_files)
6360 LOCAL_C_INCLUDES := $(dexdump_c_includes)
6461 LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
65 ifneq ($(strip $(USE_MINGW)),)
66 LOCAL_STATIC_LIBRARIES += libz
67 else
68 LOCAL_LDLIBS += -lpthread -lz
69 endif
70 # TODO: Move dexdump from libdex to libart and lose the 32-bit limitation.
71 LOCAL_32_BIT_ONLY := true
72 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
62 LOCAL_STATIC_LIBRARIES_windows += libz
63 LOCAL_LDLIBS_darwin += -lpthread -lz
64 LOCAL_LDLIBS_linux += -lpthread -lz
7365 include $(BUILD_HOST_EXECUTABLE)
4848 #include <getopt.h>
4949 #include <errno.h>
5050 #include <assert.h>
51 #include <inttypes.h>
5152
5253 static const char* gProgName = "dexdump";
5354
604605 }
605606 }
606607
607 static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
608 static int dumpPositionsCb(void * /* cnxt */, u4 address, u4 lineNum)
608609 {
609610 printf(" 0x%04x line=%d\n", address, lineNum);
610611 return 0;
626627 pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
627628 }
628629
629 static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
630 static void dumpLocalsCb(void * /* cnxt */, u2 reg, u4 startAddress,
630631 u4 endAddress, const char *name, const char *descriptor,
631632 const char *signature)
632633 {
853854 const u2* insns = pCode->insns;
854855 int i;
855856
856 printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
857 // Address of instruction (expressed as byte offset).
858 printf("%06zx:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
859
857860 for (i = 0; i < 8; i++) {
858861 if (i < insnWidth) {
859862 if (i == 7) {
938941 pDecInsn->vA, value, (u2)pDecInsn->vB);
939942 } else {
940943 s8 value = ((s8) pDecInsn->vB) << 48;
941 printf(" v%d, #long %lld // #%x",
944 printf(" v%d, #long %" PRId64 " // #%x",
942945 pDecInsn->vA, value, (u2)pDecInsn->vB);
943946 }
944947 break;
10321035 u8 j;
10331036 } conv;
10341037 conv.j = pDecInsn->vB_wide;
1035 printf(" v%d, #double %f // #%016llx",
1038 printf(" v%d, #double %f // #%016" PRIx64,
10361039 pDecInsn->vA, conv.d, pDecInsn->vB_wide);
10371040 }
10381041 break;
10641067
10651068 assert(pCode->insnsSize > 0);
10661069 insns = pCode->insns;
1070
1071 methInfo.classDescriptor =
1072 methInfo.name =
1073 methInfo.signature = NULL;
10671074
10681075 getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
10691076 startAddr = ((u1*)pCode - pDexFile->baseAddr);
+0
-51
dexlist/Android.mk less more
0 # Copyright (C) 2008 The Android Open Source Project
1 #
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at
5 #
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 #
15 # dexlist -- list all concrete methods found in a DEX file
16 #
17 LOCAL_PATH:= $(call my-dir)
18
19 dexdump_src_files := \
20 DexList.cpp
21
22 dexdump_c_includes := \
23 dalvik
24
25 dexdump_static_libraries := \
26 libdex
27
28 include $(CLEAR_VARS)
29 LOCAL_MODULE := dexlist
30 LOCAL_MODULE_TAGS := optional
31 LOCAL_SRC_FILES := $(dexdump_src_files)
32 LOCAL_C_INCLUDES := $(dexdump_c_includes)
33 LOCAL_SHARED_LIBRARIES := libcutils libz libutils
34 LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) libbase
35 LOCAL_LDLIBS +=
36 LOCAL_32_BIT_ONLY := true
37 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
38 #include $(BUILD_EXECUTABLE)
39
40 include $(CLEAR_VARS)
41 LOCAL_MODULE := dexlist
42 LOCAL_MODULE_TAGS := optional
43 LOCAL_SRC_FILES := $(dexdump_src_files)
44 LOCAL_C_INCLUDES := $(dexdump_c_includes)
45 LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) libcutils liblog libutils libbase
46 LOCAL_LDLIBS += -lpthread -lz
47 # TODO: Move dexlist from libdex to libart and lose the 32-bit limitation.
48 LOCAL_32_BIT_ONLY := true
49 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
50 include $(BUILD_HOST_EXECUTABLE)
+0
-296
dexlist/DexList.cpp less more
0 /*
1 * Copyright (C) 2008 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /*
17 * List all methods in all concrete classes in one or more DEX files.
18 */
19
20 #include "libdex/DexFile.h"
21
22 #include "libdex/CmdUtils.h"
23 #include "libdex/DexClass.h"
24 #include "libdex/DexDebugInfo.h"
25 #include "libdex/DexProto.h"
26 #include "libdex/SysUtil.h"
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <getopt.h>
35 #include <errno.h>
36 #include <assert.h>
37
38 static const char* gProgName = "dexlist";
39
40 /* command-line args */
41 static struct {
42 char* argCopy;
43 const char* classToFind;
44 const char* methodToFind;
45 } gParms;
46
47
48 /*
49 * Return a newly-allocated string for the "dot version" of the class
50 * name for the given type descriptor. That is, The initial "L" and
51 * final ";" (if any) have been removed and all occurrences of '/'
52 * have been changed to '.'.
53 */
54 static char* descriptorToDot(const char* str)
55 {
56 size_t at = strlen(str);
57 char* newStr;
58
59 if (str[0] == 'L') {
60 assert(str[at - 1] == ';');
61 at -= 2; /* Two fewer chars to copy. */
62 str++; /* Skip the 'L'. */
63 }
64
65 newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
66 newStr[at] = '\0';
67
68 while (at > 0) {
69 at--;
70 newStr[at] = (str[at] == '/') ? '.' : str[at];
71 }
72
73 return newStr;
74 }
75
76 /*
77 * Position table callback; we just want to catch the number of the
78 * first line in the method, which *should* correspond to the first
79 * entry from the table. (Could also use "min" here.)
80 */
81 static int positionsCallback(void* cnxt, u4 address, u4 lineNum)
82 {
83 int* pFirstLine = (int*) cnxt;
84 if (*pFirstLine == -1)
85 *pFirstLine = lineNum;
86 return 0;
87 }
88
89
90 /*
91 * Dump a method.
92 */
93 void dumpMethod(DexFile* pDexFile, const char* fileName,
94 const DexMethod* pDexMethod, int i)
95 {
96 const DexMethodId* pMethodId;
97 const DexCode* pCode;
98 const char* classDescriptor;
99 const char* methodName;
100 int firstLine;
101
102 /* abstract and native methods don't get listed */
103 if (pDexMethod->codeOff == 0)
104 return;
105
106 pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
107 methodName = dexStringById(pDexFile, pMethodId->nameIdx);
108
109 classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
110
111 pCode = dexGetCode(pDexFile, pDexMethod);
112 assert(pCode != NULL);
113
114 /*
115 * If the filename is empty, then set it to something printable
116 * so that it is easier to parse.
117 *
118 * TODO: A method may override its class's default source file by
119 * specifying a different one in its debug info. This possibility
120 * should be handled here.
121 */
122 if (fileName == NULL || fileName[0] == 0) {
123 fileName = "(none)";
124 }
125
126 firstLine = -1;
127 dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
128 pDexMethod->accessFlags, positionsCallback, NULL, &firstLine);
129
130 char* className = descriptorToDot(classDescriptor);
131 char* desc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
132 u4 insnsOff = pDexMethod->codeOff + offsetof(DexCode, insns);
133
134 if (gParms.methodToFind != NULL &&
135 (strcmp(gParms.classToFind, className) != 0 ||
136 strcmp(gParms.methodToFind, methodName) != 0))
137 {
138 goto skip;
139 }
140
141 printf("0x%08x %d %s %s %s %s %d\n",
142 insnsOff, pCode->insnsSize * 2,
143 className, methodName, desc,
144 fileName, firstLine);
145
146 skip:
147 free(desc);
148 free(className);
149 }
150
151 /*
152 * Run through all direct and virtual methods in the class.
153 */
154 void dumpClass(DexFile* pDexFile, int idx)
155 {
156 const DexClassDef* pClassDef;
157 DexClassData* pClassData;
158 const u1* pEncodedData;
159 const char* fileName;
160 int i;
161
162 pClassDef = dexGetClassDef(pDexFile, idx);
163 pEncodedData = dexGetClassData(pDexFile, pClassDef);
164 pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
165
166 if (pClassData == NULL) {
167 fprintf(stderr, "Trouble reading class data\n");
168 return;
169 }
170
171 if (pClassDef->sourceFileIdx == 0xffffffff) {
172 fileName = NULL;
173 } else {
174 fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
175 }
176
177 /*
178 * TODO: Each class def points at a sourceFile, so maybe that
179 * should be printed out. However, this needs to be coordinated
180 * with the tools that parse this output.
181 */
182
183 for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
184 dumpMethod(pDexFile, fileName, &pClassData->directMethods[i], i);
185 }
186
187 for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
188 dumpMethod(pDexFile, fileName, &pClassData->virtualMethods[i], i);
189 }
190
191 free(pClassData);
192 }
193
194 /*
195 * Process a file.
196 *
197 * Returns 0 on success.
198 */
199 int process(const char* fileName)
200 {
201 DexFile* pDexFile = NULL;
202 MemMapping map;
203 bool mapped = false;
204 int result = -1;
205 UnzipToFileResult utfr;
206
207 utfr = dexOpenAndMap(fileName, NULL, &map, true);
208 if (utfr != kUTFRSuccess) {
209 if (utfr == kUTFRNoClassesDex) {
210 /* no classes.dex in the APK; pretend we succeeded */
211 result = 0;
212 goto bail;
213 }
214 fprintf(stderr, "Unable to process '%s'\n", fileName);
215 goto bail;
216 }
217 mapped = true;
218
219 pDexFile = dexFileParse((u1*)map.addr, map.length, kDexParseDefault);
220 if (pDexFile == NULL) {
221 fprintf(stderr, "Warning: DEX parse failed for '%s'\n", fileName);
222 goto bail;
223 }
224
225 printf("#%s\n", fileName);
226
227 int i;
228 for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
229 dumpClass(pDexFile, i);
230 }
231
232 result = 0;
233
234 bail:
235 if (mapped)
236 sysReleaseShmem(&map);
237 if (pDexFile != NULL)
238 dexFileFree(pDexFile);
239 return result;
240 }
241
242
243 /*
244 * Show usage.
245 */
246 void usage(void)
247 {
248 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
249 fprintf(stderr, "%s: dexfile [dexfile2 ...]\n", gProgName);
250 fprintf(stderr, "\n");
251 }
252
253 /*
254 * Parse args.
255 */
256 int main(int argc, char* const argv[])
257 {
258 int result = 0;
259 int i;
260
261 /*
262 * Find all instances of the fully-qualified method name. This isn't
263 * really what dexlist is for, but it's easy to do it here.
264 */
265 if (argc > 3 && strcmp(argv[1], "--method") == 0) {
266 gParms.argCopy = strdup(argv[2]);
267 char* meth = strrchr(gParms.argCopy, '.');
268 if (meth == NULL) {
269 fprintf(stderr, "Expected package.Class.method\n");
270 free(gParms.argCopy);
271 return 2;
272 }
273 *meth = '\0';
274 gParms.classToFind = gParms.argCopy;
275 gParms.methodToFind = meth+1;
276 argv += 2;
277 argc -= 2;
278 }
279
280 if (argc < 2) {
281 fprintf(stderr, "%s: no file specified\n", gProgName);
282 usage();
283 return 2;
284 }
285
286 /*
287 * Run through the list of files. If one of them fails we contine on,
288 * only returning a failure at the end.
289 */
290 for (i = 1; i < argc; i++)
291 result |= process(argv[i]);
292
293 free(gParms.argCopy);
294 return result;
295 }
101101 fi
102102
103103 if [ ! -r "${proguard}" ]; then
104 proguard="${ANDROID_BUILD_TOP}"/external/proguard/bin/${proguardExec}
105 fi
106
107 if [ ! -r "${proguard}" ]; then
104108 proguard="`which proguard`"
105109 fi
106110
2020 */
2121 public class Version {
2222 /** {@code non-null;} version string */
23 public static final String VERSION = "1.11";
23 public static final String VERSION = "1.12";
2424 }
140140 * actually present on the stack.</p>
141141 *
142142 * <p>In the case where there is a known-null on the stack where
143 * an array is expected, we just fall back to the implied type of
144 * the instruction. Due to the quirk described above, this means
145 * that source code that uses <code>boolean[]</code> might get
146 * translated surprisingly -- but correctly -- into an instruction
147 * that specifies a <code>byte[]</code>. It will be correct,
148 * because should the code actually execute, it will necessarily
149 * throw a <code>NullPointerException</code>, and it won't matter
150 * what opcode variant is used to achieve that result.</p>
143 * an array is expected, our behavior depends on the implied type
144 * of the instruction. When the implied type is a reference, we
145 * don't attempt to infer anything, as we don't know the dimension
146 * of the null constant and thus any explicit inferred type could
147 * be wrong. When the implied type is a primitive, we fall back to
148 * the implied type of the instruction. Due to the quirk described
149 * above, this means that source code that uses
150 * <code>boolean[]</code> might get translated surprisingly -- but
151 * correctly -- into an instruction that specifies a
152 * <code>byte[]</code>. It will be correct, because should the
153 * code actually execute, it will necessarily throw a
154 * <code>NullPointerException</code>, and it won't matter what
155 * opcode variant is used to achieve that result.</p>
151156 *
152157 * @param impliedType {@code non-null;} type implied by the
153158 * instruction; is <i>not</i> an array type
159164 private static Type requiredArrayTypeFor(Type impliedType,
160165 Type foundArrayType) {
161166 if (foundArrayType == Type.KNOWN_NULL) {
162 return impliedType.getArrayType();
167 return impliedType.isReference()
168 ? Type.KNOWN_NULL
169 : impliedType.getArrayType();
163170 }
164171
165172 if ((impliedType == Type.OBJECT)
316323 requiredArrayTypeFor(type, foundArrayType);
317324
318325 // Make type agree with the discovered requiredArrayType.
319 type = requiredArrayType.getComponentType();
326 type = (requiredArrayType == Type.KNOWN_NULL)
327 ? Type.KNOWN_NULL
328 : requiredArrayType.getComponentType();
320329
321330 machine.popArgs(frame, requiredArrayType, Type.INT);
322331 break;
374383 * if it has local info.
375384 */
376385 if (foundArrayLocal) {
377 type = requiredArrayType.getComponentType();
386 type = (requiredArrayType == Type.KNOWN_NULL)
387 ? Type.KNOWN_NULL
388 : requiredArrayType.getComponentType();
378389 }
379390
380391 machine.popArgs(frame, requiredArrayType, Type.INT, type);
414414 }
415415
416416 /**
417 * Sees if the .class file header magic/version are within
417 * Sees if the .class file header magic has the good value.
418 *
419 * @param magic the value of a classfile "magic" field
420 * @return true if the magic is valid
421 */
422 private boolean isGoodMagic(int magic) {
423 return magic == CLASS_FILE_MAGIC;
424 }
425
426 /**
427 * Sees if the .class file header version are within
418428 * range.
419429 *
420 * @param magic the value of a classfile "magic" field
421430 * @param minorVersion the value of a classfile "minor_version" field
422431 * @param majorVersion the value of a classfile "major_version" field
423 * @return true iff the parameters are valid and within range
424 */
425 private boolean isGoodVersion(int magic, int minorVersion,
426 int majorVersion) {
432 * @return true if the parameters are valid and within range
433 */
434 private boolean isGoodVersion(int minorVersion, int majorVersion) {
427435 /* Valid version ranges are typically of the form
428436 * "A.0 through B.C inclusive" where A <= B and C >= 0,
429437 * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
430438 */
431 if (magic == CLASS_FILE_MAGIC && minorVersion >= 0) {
439 if (minorVersion >= 0) {
432440 /* Check against max first to handle the case where
433441 * MIN_MAJOR == MAX_MAJOR.
434442 */
466474 /* Make sure that this looks like a valid class file with a
467475 * version that we can handle.
468476 */
469 if (!isGoodVersion(getMagic0(), getMinorVersion0(),
470 getMajorVersion0())) {
471 throw new ParseException("bad class file magic (" +
472 Hex.u4(getMagic0()) +
473 ") or version (" +
474 Hex.u2(getMajorVersion0()) + "." +
475 Hex.u2(getMinorVersion0()) + ")");
477 if (!isGoodMagic(getMagic0())) {
478 throw new ParseException("bad class file magic (" + Hex.u4(getMagic0()) + ")");
479 }
480
481 if (!isGoodVersion(getMinorVersion0(), getMajorVersion0())) {
482 throw new ParseException("unsupported class file version " +
483 getMajorVersion0() + "." +
484 getMinorVersion0());
476485 }
477486 }
478487
594594 // class translation and adding to dex.
595595 int count = errors.incrementAndGet();
596596 if (count < 10) {
597 DxConsole.err.println("Uncaught translation error: " + ex.getCause());
597 if (args.debug) {
598 DxConsole.err.println("Uncaught translation error:");
599 ex.getCause().printStackTrace(DxConsole.err);
600 } else {
601 DxConsole.err.println("Uncaught translation error: " + ex.getCause());
602 }
598603 } else {
599604 throw new InterruptedException("Too many errors");
600605 }
747752 try {
748753 new DirectClassFileConsumer(name, bytes, null).call(
749754 new ClassParserTask(name, bytes).call());
755 } catch (ParseException ex) {
756 // handled in FileBytesConsumer
757 throw ex;
750758 } catch(Exception ex) {
751759 throw new RuntimeException("Exception parsing classes", ex);
752760 }
16521660 DxConsole.err.println("\nEXCEPTION FROM SIMULATION:");
16531661 DxConsole.err.println(ex.getMessage() + "\n");
16541662 DxConsole.err.println(((SimException) ex).getContext());
1663 } else if (ex instanceof ParseException) {
1664 DxConsole.err.println("\nPARSE ERROR:");
1665 ParseException parseException = (ParseException) ex;
1666 if (args.debug) {
1667 parseException.printStackTrace(DxConsole.err);
1668 } else {
1669 parseException.printContext(DxConsole.err);
1670 }
16551671 } else {
16561672 DxConsole.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
16571673 ex.printStackTrace(DxConsole.err);
167167 contentsOut.header.size = 1;
168168 contentsOut.fileSize = dexOut.getLength();
169169 contentsOut.computeSizesFromOffsets();
170 contentsOut.writeHeader(headerOut);
170 contentsOut.writeHeader(headerOut, mergeApiLevels());
171171 contentsOut.writeMap(mapListOut);
172172
173173 // generate and write the hashes
354354 return value.compareTo(unsortedValue.value);
355355 }
356356 }
357 }
358
359 private int mergeApiLevels() {
360 int maxApi = -1;
361 for (int i = 0; i < dexes.length; i++) {
362 int dexMinApi = dexes[i].getTableOfContents().apiLevel;
363 if (maxApi < dexMinApi) {
364 maxApi = dexMinApi;
365 }
366 }
367 return maxApi;
357368 }
358369
359370 private void mergeStringIds() {
10561067 } else {
10571068 // at most 1/4 of the bytes in a code section are uleb/sleb
10581069 code += (int) Math.ceil(contents.codes.byteCount * 1.25);
1059 // at most 1/3 of the bytes in a class data section are uleb/sleb
1060 classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34);
1070 // at most 2/3 of the bytes in a class data section are uleb/sleb that may change
1071 // (assuming the worst case that section contains only methods and no fields)
1072 classData += (int) Math.ceil(contents.classDatas.byteCount * 1.67);
10611073 // all of the bytes in an encoding arrays section may be uleb/sleb
10621074 encodedArray += contents.encodedArrays.byteCount * 2;
10631075 // all of the bytes in an annotations section may be uleb/sleb
1616 package com.android.multidex;
1717
1818 import com.android.dx.cf.direct.DirectClassFile;
19 import com.android.dx.cf.iface.FieldList;
20 import com.android.dx.cf.iface.MethodList;
1921 import com.android.dx.rop.cst.Constant;
20 import com.android.dx.rop.cst.ConstantPool;
22 import com.android.dx.rop.cst.CstBaseMethodRef;
2123 import com.android.dx.rop.cst.CstFieldRef;
22 import com.android.dx.rop.cst.CstMethodRef;
2324 import com.android.dx.rop.cst.CstType;
2425 import com.android.dx.rop.type.Prototype;
2526 import com.android.dx.rop.type.StdTypeList;
26 import com.android.dx.rop.type.Type;
2727 import com.android.dx.rop.type.TypeList;
2828
2929 import java.io.FileNotFoundException;
8686 throw new IOException("Class " + name +
8787 " is missing form original class path " + path, e);
8888 }
89
90 addDependencies(classFile.getConstantPool());
89 addDependencies(classFile);
9190 }
9291 }
9392 }
9695 return classNames;
9796 }
9897
99 private void addDependencies(ConstantPool pool) {
100 for (Constant constant : pool.getEntries()) {
98 private void addDependencies(DirectClassFile classFile) {
99 for (Constant constant : classFile.getConstantPool().getEntries()) {
101100 if (constant instanceof CstType) {
102 checkDescriptor(((CstType) constant).getClassType());
101 checkDescriptor(((CstType) constant).getClassType().getDescriptor());
103102 } else if (constant instanceof CstFieldRef) {
104 checkDescriptor(((CstFieldRef) constant).getType());
105 } else if (constant instanceof CstMethodRef) {
106 Prototype proto = ((CstMethodRef) constant).getPrototype();
107 checkDescriptor(proto.getReturnType());
108 StdTypeList args = proto.getParameterTypes();
109 for (int i = 0; i < args.size(); i++) {
110 checkDescriptor(args.get(i));
111 }
103 checkDescriptor(((CstFieldRef) constant).getType().getDescriptor());
104 } else if (constant instanceof CstBaseMethodRef) {
105 checkPrototype(((CstBaseMethodRef) constant).getPrototype());
112106 }
107 }
108
109 FieldList fields = classFile.getFields();
110 int nbField = fields.size();
111 for (int i = 0; i < nbField; i++) {
112 checkDescriptor(fields.get(i).getDescriptor().getString());
113 }
114
115 MethodList methods = classFile.getMethods();
116 int nbMethods = methods.size();
117 for (int i = 0; i < nbMethods; i++) {
118 checkPrototype(Prototype.intern(methods.get(i).getDescriptor().getString()));
113119 }
114120 }
115121
116 private void checkDescriptor(Type type) {
117 String descriptor = type.getDescriptor();
118 if (descriptor.endsWith(";")) {
119 int lastBrace = descriptor.lastIndexOf('[');
122 private void checkPrototype(Prototype proto) {
123 checkDescriptor(proto.getReturnType().getDescriptor());
124 StdTypeList args = proto.getParameterTypes();
125 for (int i = 0; i < args.size(); i++) {
126 checkDescriptor(args.get(i).getDescriptor());
127 }
128 }
129
130 private void checkDescriptor(String typeDescriptor) {
131 if (typeDescriptor.endsWith(";")) {
132 int lastBrace = typeDescriptor.lastIndexOf('[');
120133 if (lastBrace < 0) {
121 addClassWithHierachy(descriptor.substring(1, descriptor.length()-1));
134 addClassWithHierachy(typeDescriptor.substring(1, typeDescriptor.length()-1));
122135 } else {
123 assert descriptor.length() > lastBrace + 3
124 && descriptor.charAt(lastBrace + 1) == 'L';
125 addClassWithHierachy(descriptor.substring(lastBrace + 2,
126 descriptor.length() - 1));
136 assert typeDescriptor.length() > lastBrace + 3
137 && typeDescriptor.charAt(lastBrace + 1) == 'L';
138 addClassWithHierachy(typeDescriptor.substring(lastBrace + 2,
139 typeDescriptor.length() - 1));
127140 }
128141 }
129142 }
1313 # See the License for the specific language governing permissions and
1414 # limitations under the License.
1515
16 dx --junit com.android.dx.util._tests._Bits > unit-out.txt
16 prog=`which dx`
17 progdir=`dirname "${prog}"`
18 dxjar=$progdir/../framework/dx.jar
19 dxtestsjar=$progdir/../framework/dx-tests.jar
20 junitjar=$progdir/../framework/junit.jar
21
22 java -cp $dxtestsjar:$dxjar:$junitjar junit.textui.TestRunner com.android.dx.util.BitsTest > unit-out.txt 2>&1
1723
1824 if [ "$?" = "0" ]; then
1925 echo "Yay!"
1313 # See the License for the specific language governing permissions and
1414 # limitations under the License.
1515
16 dx --junit com.android.dx.util._tests._IntList > unit-out.txt
16 prog=`which dx`
17 progdir=`dirname "${prog}"`
18 dxjar=$progdir/../framework/dx.jar
19 dxtestsjar=$progdir/../framework/dx-tests.jar
20 junitjar=$progdir/../framework/junit.jar
21
22 java -cp $dxtestsjar:$dxjar:$junitjar junit.textui.TestRunner com.android.dx.util.IntListTest > unit-out.txt 2>&1
1723
1824 if [ "$?" = "0" ]; then
1925 echo "Yay!"
dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class less more
Binary diff not shown
113113 0003: const/16 v2, #int 16 // #0010
114114 0005: aput v2, v0, v1
115115 0007: return-void
116 multidimensional.test_getBooleanArray:()Z:
117 regs: 0002; ins: 0000; outs: 0000
118 0000: const/4 v1, #int 1 // #1
119 0001: const/4 v0, #null // #0
120 0002: aget-object v0, v0, v1
121 0004: aget-byte v0, v0, v1
122 0006: return v0
123 multidimensional.test_getByteArray:()B:
124 regs: 0002; ins: 0000; outs: 0000
125 0000: const/4 v1, #int 1 // #1
126 0001: const/4 v0, #null // #0
127 0002: aget-object v0, v0, v1
128 0004: aget-byte v0, v0, v1
129 0006: return v0
130 multidimensional.test_getCharArray:()C:
131 regs: 0002; ins: 0000; outs: 0000
132 0000: const/4 v1, #int 1 // #1
133 0001: const/4 v0, #null // #0
134 0002: aget-object v0, v0, v1
135 0004: aget-char v0, v0, v1
136 0006: return v0
137 multidimensional.test_getDoubleArray:()D:
138 regs: 0002; ins: 0000; outs: 0000
139 0000: const/4 v1, #int 1 // #1
140 0001: const/4 v0, #null // #0
141 0002: aget-object v0, v0, v1
142 0004: aget-wide v0, v0, v1
143 0006: return-wide v0
144 multidimensional.test_getFloatArray:()F:
145 regs: 0002; ins: 0000; outs: 0000
146 0000: const/4 v1, #int 1 // #1
147 0001: const/4 v0, #null // #0
148 0002: aget-object v0, v0, v1
149 0004: aget v0, v0, v1
150 0006: return v0
151 multidimensional.test_getIntArray:()I:
152 regs: 0002; ins: 0000; outs: 0000
153 0000: const/4 v1, #int 1 // #1
154 0001: const/4 v0, #null // #0
155 0002: aget-object v0, v0, v1
156 0004: aget v0, v0, v1
157 0006: return v0
158 multidimensional.test_getLongArray:()J:
159 regs: 0002; ins: 0000; outs: 0000
160 0000: const/4 v1, #int 1 // #1
161 0001: const/4 v0, #null // #0
162 0002: aget-object v0, v0, v1
163 0004: aget-wide v0, v0, v1
164 0006: return-wide v0
165 multidimensional.test_getObjectArray:()Ljava/lang/Object;:
166 regs: 0002; ins: 0000; outs: 0000
167 0000: const/4 v1, #int 1 // #1
168 0001: const/4 v0, #null // #0
169 0002: aget-object v0, v0, v1
170 0004: aget-object v0, v0, v1
171 0006: return-object v0
172 multidimensional.test_getShortArray:()S:
173 regs: 0002; ins: 0000; outs: 0000
174 0000: const/4 v1, #int 1 // #1
175 0001: const/4 v0, #null // #0
176 0002: aget-object v0, v0, v1
177 0004: aget-short v0, v0, v1
178 0006: return v0
179 multidimensional.test_setBooleanArray:()V:
180 regs: 0003; ins: 0000; outs: 0000
181 0000: const/4 v2, #int 1 // #1
182 0001: const/4 v0, #null // #0
183 0002: aget-object v0, v0, v2
184 0004: const/4 v1, #int 0 // #0
185 0005: aput v1, v0, v2
186 0007: return-void
187 multidimensional.test_setByteArray:()V:
188 regs: 0003; ins: 0000; outs: 0000
189 0000: const/4 v2, #int 1 // #1
190 0001: const/4 v0, #null // #0
191 0002: aget-object v0, v0, v2
192 0004: const/4 v1, #int 0 // #0
193 0005: aput v1, v0, v2
194 0007: return-void
195 multidimensional.test_setCharArray:()V:
196 regs: 0003; ins: 0000; outs: 0000
197 0000: const/4 v2, #int 1 // #1
198 0001: const/4 v0, #null // #0
199 0002: aget-object v0, v0, v2
200 0004: const/4 v1, #int 0 // #0
201 0005: aput v1, v0, v2
202 0007: return-void
203 multidimensional.test_setDoubleArray:()V:
204 regs: 0004; ins: 0000; outs: 0000
205 0000: const/4 v1, #int 1 // #1
206 0001: const/4 v0, #null // #0
207 0002: aget-object v0, v0, v1
208 0004: const-wide/16 v2, #double 0.0 // #0000
209 0006: aput-wide v2, v0, v1
210 0008: return-void
211 multidimensional.test_setFloatArray:()V:
212 regs: 0003; ins: 0000; outs: 0000
213 0000: const/4 v2, #int 1 // #1
214 0001: const/4 v0, #null // #0
215 0002: aget-object v0, v0, v2
216 0004: const/4 v1, #float 0.0 // #0
217 0005: aput v1, v0, v2
218 0007: return-void
219 multidimensional.test_setIntArray:()V:
220 regs: 0003; ins: 0000; outs: 0000
221 0000: const/4 v2, #int 1 // #1
222 0001: const/4 v0, #null // #0
223 0002: aget-object v0, v0, v2
224 0004: const/4 v1, #int 0 // #0
225 0005: aput v1, v0, v2
226 0007: return-void
227 multidimensional.test_setLongArray:()V:
228 regs: 0004; ins: 0000; outs: 0000
229 0000: const/4 v1, #int 1 // #1
230 0001: const/4 v0, #null // #0
231 0002: aget-object v0, v0, v1
232 0004: const-wide/16 v2, #long 0 // #0000
233 0006: aput-wide v2, v0, v1
234 0008: return-void
235 multidimensional.test_setObjectArray:()V:
236 regs: 0003; ins: 0000; outs: 0000
237 0000: const/4 v2, #null // #0
238 0001: const/4 v1, #int 1 // #1
239 0002: aget-object v0, v2, v1
240 0004: aput-object v2, v0, v1
241 0006: return-void
242 multidimensional.test_setShortArray:()V:
243 regs: 0003; ins: 0000; outs: 0000
244 0000: const/4 v2, #int 1 // #1
245 0001: const/4 v0, #null // #0
246 0002: aget-object v0, v0, v2
247 0004: const/4 v1, #int 0 // #0
248 0005: aput v1, v0, v2
249 0007: return-void
0 #!/bin/bash
1 #
2 # Copyright (C) 2015 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 echo '
17 .class multidimensional
18 .super java/lang/Object
19 '
20
21 function onetype() {
22 local typename=$1
23 local stacksize=$2
24 local defaultvalue=$3
25 local descriptor=$4
26 local defaultload=$5
27 local loadstoreprefix=$6
28 local returnprefix=${7:-$loadstoreprefix}
29 echo "
30 ; Output from some versions of javac on:
31 ; public static $typename test_get${typename^}Array() {
32 ; $typename[][] array = null;
33 ; return array[1][1];
34 ; }
35 .method public static test_get${typename^}Array()$descriptor
36 .limit locals 1
37 .limit stack 2
38
39 aconst_null
40 astore_0
41 aload_0
42 iconst_1
43 aaload
44 iconst_1
45 ${loadstoreprefix}aload
46 ${returnprefix}return
47 .end method
48
49 ; Output from some versions of javac on:
50 ; public static void test_set${typename^}Array() {
51 ; $typename[][] array = null;
52 ; array[1][1] = $defaultvalue;
53 ; }
54 .method public static test_set${typename^}Array()V
55 .limit locals 1
56 .limit stack $((stacksize+2))
57
58 aconst_null
59 astore_0
60 aload_0
61 iconst_1
62 aaload
63 iconst_1
64 $defaultload
65 ${loadstoreprefix}astore
66 return
67 .end method
68 "
69 }
70
71 onetype Object 1 null 'Ljava/lang/Object;' aconst_null a
72 onetype boolean 1 false Z iconst_0 b i
73 onetype byte 1 0 B iconst_0 b i
74 onetype char 1 0 C iconst_0 c i
75 onetype short 1 0 S iconst_0 s i
76 onetype int 1 0 I iconst_0 i
77 onetype long 2 0 J lconst_0 l
78 onetype float 1 0 F fconst_0 f
79 onetype double 2 0 D dconst_0 d
1515
1616 $JAVAC -g -d . Blort.java
1717 dx --debug --dex --positions=none --no-locals \
18 --dump-to=- --dump-method="Blort.test*" *.class
18 --dump-to=- --dump-method="Blort.test*" Blort.class
19
20 bash multidimensional.sh > multidimensional.j
21 jasmin -d . multidimensional.j >/dev/null
22 dx --debug --dex --positions=none --no-locals \
23 --dump-to=- --dump-method="multidimensional.*" multidimensional.class
1717 prog=`which dx`
1818 progdir=`dirname "${prog}"`
1919 dxjar=$progdir/../framework/dx.jar
20 junitjar=$progdir/../framework/junit.jar
21 junitdex=$progdir/../framework/junit-hostdex.jar
2022
21 javac -cp $dxjar `find . -name "*.java"`
23 javac -cp $dxjar:$junitjar `find . -name "*.java"`
2224 dx --dex --output=test.jar com/android/dx/merge/* $dxjar
2325
2426 # Build a resource .jar containing the .dex files to merge
2931 dx --dex --output=testdata/TryCatchFinally.dex testdata/TryCatchFinally*
3032 jar cfM resources.jar testdata/*.dex
3133
32 dalvik -classpath test.jar:resources.jar \
33 junit.textui.TestRunner com.android.dx.merge.DexMergeTest > unit-out.txt
34 art -classpath test.jar:$junitdex:resources.jar \
35 junit.textui.TestRunner com.android.dx.merge.DexMergeTest > unit-out.txt 2>&1
3436
3537 if [ "$?" = "0" ]; then
3638 echo "Yay!"
1717 prog=`which dx`
1818 progdir=`dirname "${prog}"`
1919 dxjar=$progdir/../framework/dx.jar
20 junitjar=$progdir/../framework/junit.jar
2021
21 javac -cp $dxjar `find . -name "*.java"`
22 java -classpath $dxjar:. junit.textui.TestRunner com.android.dx.util.Leb128UtilsTest > unit-out.txt
22 javac -cp $dxjar:$junitjar `find . -name "*.java"`
23 java -classpath $dxjar:$junitjar:. junit.textui.TestRunner com.android.dx.util.Leb128UtilsTest > unit-out.txt 2>&1
2324
2425 if [ "$?" = "0" ]; then
2526 echo "Yay!"
1717 prog=`which dx`
1818 progdir=`dirname "${prog}"`
1919 dxjar=$progdir/../framework/dx.jar
20 junitjar=$progdir/../framework/junit.jar
2021
21 javac -cp $dxjar `find . -name "*.java"`
22 java -classpath $dxjar:. junit.textui.TestRunner com.android.dx.util.Mutf8Test > unit-out.txt
22 javac -cp $dxjar:$junitjar `find . -name "*.java"`
23 java -classpath $dxjar:$junitjar:. junit.textui.TestRunner com.android.dx.util.Mutf8Test > unit-out.txt 2>&1
2324
2425 if [ "$?" = "0" ]; then
2526 echo "Yay!"
1717 prog=`which dx`
1818 progdir=`dirname "${prog}"`
1919 dxjar=$progdir/../framework/dx.jar
20 junitjar=$progdir/../framework/junit.jar
21 junitdex=$progdir/../framework/junit-hostdex.jar
2022
21 javac -cp $dxjar `find . -name "*.java"`
23 javac -cp $dxjar:$junitjar `find . -name "*.java"`
2224 dx --dex --output=test.jar com/android/dx/merge/* $dxjar
2325
2426 # Build a resource .jar containing the .dex files to merge
2628 dx --dex --output=testdata/B.dex testdata/B.class
2729 jar cfM resources.jar testdata/*.dex
2830
29 dalvik -classpath test.jar:resources.jar \
30 junit.textui.TestRunner com.android.dx.merge.MergeConflictTest > unit-out.txt
31 art -classpath test.jar:$junitdex:resources.jar \
32 junit.textui.TestRunner com.android.dx.merge.MergeConflictTest > unit-out.txt 2>&1
3133
3234 if [ "$?" = "0" ]; then
3335 echo "Yay!"
dx/tests/123-dex-transform-invalid-virtual-to-direct/Zorch.class less more
Binary diff not shown
2323 dx -JXmx1024m --dex --no-optimize --multi-dex --main-dex-list=main.list --minimal-main-dex --output=out classes
2424
2525
26 dalvik -classpath test.jar com.android.dx.multidex.MainDexListTest > unit-out.txt
26 art -classpath test.jar com.android.dx.multidex.MainDexListTest > unit-out.txt 2>&1
2727
2828 if [ "$?" = "0" ]; then
2929 echo "Yay!"
5555 LOCAL_MODULE_TAGS := optional
5656 LOCAL_MODULE := libdex
5757 LOCAL_32_BIT_ONLY := true
58 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
5958 include $(BUILD_STATIC_LIBRARY)
6059
6160 endif # !SDK_ONLY
7271 LOCAL_CPPFLAGS := -std=gnu++11
7372 LOCAL_STATIC_LIBRARIES := liblog libutils
7473 LOCAL_WHOLE_STATIC_LIBRARIES := libziparchive-host
75 LOCAL_MODULE_TAGS := optional
74 LOCAL_MODULE_HOST_OS := darwin linux windows
7675 LOCAL_MODULE := libdex
77 LOCAL_32_BIT_ONLY := true
78 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
7976 include $(BUILD_HOST_STATIC_LIBRARY)
5353 goto bail;
5454 }
5555
56 fd = open(outFileName, O_RDWR | O_CREAT | O_EXCL, 0600);
56 fd = open(outFileName, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
5757 if (fd < 0) {
5858 fprintf(stderr, "Unable to create output file '%s': %s\n",
5959 outFileName, strerror(errno));
3535 /*
3636 * Avoiding pulling in safe_iop for safe_iopf.
3737 */
38 if (!safe_mul(&size, maxCount, sizeof(u4) + sizeof(u2)) ||
38 const u4 sizeOfItems = (u4) (sizeof(u4) + sizeof(u2));
39 if (!safe_mul(&size, maxCount, sizeOfItems) ||
3940 !safe_add(&size, size, sizeof(DexDataMap))) {
4041 return NULL;
4142 }
8585
8686 /* DEX file magic number */
8787 #define DEX_MAGIC "dex\n"
88
89 /* The version for android N, encoded in 4 bytes of ASCII. This differentiates dex files that may
90 * use default methods.
91 */
92 #define DEX_MAGIC_VERS_37 "037\0"
8893
8994 /* current version, encoded in 4 bytes of ASCII */
9095 #define DEX_MAGIC_VERS "036\0"
377377
378378 SWAP_FIELD4(pMap->size);
379379 count = pMap->size;
380
381 CHECK_LIST_SIZE(item, count, sizeof(DexMapItem));
380 const u4 sizeOfItem = (u4) sizeof(DexMapItem);
381 CHECK_LIST_SIZE(item, count, sizeOfItem);
382382
383383 while (count--) {
384384 SWAP_FIELD2(item->type);
10431043 bool first = true;
10441044 u4 lastIdx = 0;
10451045
1046 CHECK_LIST_SIZE(item, count, sizeof(DexFieldAnnotationsItem));
1046 const u4 sizeOfItem = (u4) sizeof(DexFieldAnnotationsItem);
1047 CHECK_LIST_SIZE(item, count, sizeOfItem);
10471048
10481049 while (count--) {
10491050 SWAP_INDEX4(item->fieldIdx, state->pHeader->fieldIdsSize);
10721073 bool first = true;
10731074 u4 lastIdx = 0;
10741075
1075 CHECK_LIST_SIZE(item, count, sizeof(DexMethodAnnotationsItem));
1076 const u4 sizeOfItem = (u4) sizeof(DexMethodAnnotationsItem);
1077 CHECK_LIST_SIZE(item, count, sizeOfItem);
10761078
10771079 while (count--) {
10781080 SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
11021104 bool first = true;
11031105 u4 lastIdx = 0;
11041106
1105 CHECK_LIST_SIZE(item, count, sizeof(DexParameterAnnotationsItem));
1107 const u4 sizeOfItem = (u4) sizeof(DexParameterAnnotationsItem);
1108 CHECK_LIST_SIZE(item, count, sizeOfItem);
11061109
11071110 while (count--) {
11081111 SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
13041307 SWAP_FIELD4(pTypeList->size);
13051308 count = pTypeList->size;
13061309 pType = pTypeList->list;
1307 CHECK_LIST_SIZE(pType, count, sizeof(DexTypeItem));
1310
1311 const u4 sizeOfItem = (u4) sizeof(DexTypeItem);
1312 CHECK_LIST_SIZE(pType, count, sizeOfItem);
13081313
13091314 while (count--) {
13101315 SWAP_INDEX2(pType->typeIdx, state->pHeader->typeIdsSize);
13251330 SWAP_FIELD4(list->size);
13261331 count = list->size;
13271332 item = list->list;
1328 CHECK_LIST_SIZE(item, count, sizeof(DexAnnotationSetRefItem));
1333
1334 const u4 sizeOfItem = (u4) sizeof(DexAnnotationSetRefItem);
1335 CHECK_LIST_SIZE(item, count, sizeOfItem);
13291336
13301337 while (count--) {
13311338 SWAP_OFFSET4(item->annotationsOff);
13641371 SWAP_FIELD4(set->size);
13651372 count = set->size;
13661373 item = set->entries;
1367 CHECK_LIST_SIZE(item, count, sizeof(u4));
1374
1375 const u4 sizeOfItem = (u4) sizeof(u4);
1376 CHECK_LIST_SIZE(item, count, sizeOfItem);
13681377
13691378 while (count--) {
13701379 SWAP_OFFSET4(*item);
17381747 u4 count = code->triesSize;
17391748 u4 lastEnd = 0;
17401749
1741 CHECK_LIST_SIZE(tries, count, sizeof(DexTry));
1750 const u4 sizeOfItem = (u4) sizeof(DexTry);
1751 CHECK_LIST_SIZE(tries, count, sizeOfItem);
17421752
17431753 while (count--) {
17441754 u4 i;
18171827
18181828 count = item->insnsSize;
18191829 insns = item->insns;
1820 CHECK_LIST_SIZE(insns, count, sizeof(u2));
1830
1831 const u4 sizeOfItem = (u4) sizeof(u2);
1832 CHECK_LIST_SIZE(insns, count, sizeOfItem);
18211833
18221834 while (count--) {
18231835 *insns = SWAP2(*insns);
27792791 }
27802792
27812793 if ((memcmp(version, DEX_MAGIC_VERS, 4) != 0) &&
2782 (memcmp(version, DEX_MAGIC_VERS_API_13, 4) != 0)) {
2794 (memcmp(version, DEX_MAGIC_VERS_API_13, 4) != 0) &&
2795 (memcmp(version, DEX_MAGIC_VERS_37, 4) != 0)) {
27832796 /*
27842797 * Magic was correct, but this is an unsupported older or
27852798 * newer format variant.
6969 */
7070 DEX_INLINE int dexZipFindEntry(const ZipArchiveHandle pArchive,
7171 const char* entryName, ZipEntry* data) {
72 return FindEntry(pArchive, ZipEntryName(entryName), data);
72 return FindEntry(pArchive, ZipString(entryName), data);
7373 }
7474
7575 /*
1717
1818 import java.io.IOException;
1919 import java.io.RandomAccessFile;
20 import java.nio.charset.StandardCharsets;
2021 import java.util.Arrays;
2122
2223 /**
6566 * Verifies the given magic number.
6667 */
6768 private static boolean verifyMagic(byte[] magic) {
68 return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC) ||
69 Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_API_13);
69 return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v035) ||
70 Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v037);
7071 }
7172
7273 /**
537538 public int classDefsSize, classDefsOff;
538539
539540 /* expected magic values */
540 public static final byte[] DEX_FILE_MAGIC = {
541 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x36, 0x00 };
542 public static final byte[] DEX_FILE_MAGIC_API_13 = {
543 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
541 public static final byte[] DEX_FILE_MAGIC_v035 =
542 "dex\n035\0".getBytes(StandardCharsets.US_ASCII);
543
544 // Dex version 036 skipped because of an old dalvik bug on some versions
545 // of android where dex files with that version number would erroneously
546 // be accepted and run. See: art/runtime/dex_file.cc
547
548 // V037 was introduced in API LEVEL 24
549 public static final byte[] DEX_FILE_MAGIC_v037 =
550 "dex\n037\0".getBytes(StandardCharsets.US_ASCII);
544551 public static final int ENDIAN_CONSTANT = 0x12345678;
545552 public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
546553 }
+0
-22
tools/dmtracedump/Android.mk less more
0 #
1 # Copyright 2006 The Android Open Source Project
2 #
3 # Java method trace dump tool
4 #
5
6 LOCAL_PATH:= $(call my-dir)
7
8
9 include $(CLEAR_VARS)
10 LOCAL_SRC_FILES := TraceDump.c
11 LOCAL_CFLAGS += -O0 -g
12 LOCAL_MODULE_TAGS := optional
13 LOCAL_MODULE := dmtracedump
14 include $(BUILD_HOST_EXECUTABLE)
15
16 include $(CLEAR_VARS)
17 LOCAL_SRC_FILES := CreateTestTrace.c
18 LOCAL_CFLAGS += -O0 -g
19 LOCAL_MODULE_TAGS := optional
20 LOCAL_MODULE := create_test_dmtrace
21 include $(BUILD_HOST_EXECUTABLE)
+0
-493
tools/dmtracedump/CreateTestTrace.c less more
0 /* //device/tools/dmtracedump/CreateTrace.c
1 **
2 ** Copyright 2006, 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 * Create a test file in the format required by dmtrace.
19 */
20 #define NOT_VM
21 #include "Profile.h" // from VM header
22
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <ctype.h>
33
34 /*
35 * Values from the header of the data file.
36 */
37 typedef struct DataHeader {
38 unsigned int magic;
39 short version;
40 short offsetToData;
41 long long startWhen;
42 } DataHeader;
43
44 #define VERSION 2
45 int versionNumber = VERSION;
46 int verbose = 0;
47
48 DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL };
49
50 char *versionHeader = "*version\n";
51 char *clockDef = "clock=thread-cpu\n";
52
53 char *keyThreads =
54 "*threads\n"
55 "1 main\n"
56 "2 foo\n"
57 "3 bar\n"
58 "4 blah\n";
59
60 char *keyEnd = "*end\n";
61
62 typedef struct dataRecord {
63 unsigned int time;
64 int threadId;
65 unsigned int action; /* 0=entry, 1=exit, 2=exception exit */
66 char *fullName;
67 char *className;
68 char *methodName;
69 char *signature;
70 unsigned int methodId;
71 } dataRecord;
72
73 dataRecord *records;
74
75 #define BUF_SIZE 1024
76 char buf[BUF_SIZE];
77
78 typedef struct stack {
79 dataRecord **frames;
80 int indentLevel;
81 } stack;
82
83 /* Mac OS doesn't have strndup(), so implement it here.
84 */
85 char *strndup(const char *src, size_t len)
86 {
87 char *dest = (char *) malloc(len + 1);
88 strncpy(dest, src, len);
89 dest[len] = 0;
90 return dest;
91 }
92
93 /*
94 * Parse the input file. It looks something like this:
95 * # This is a comment line
96 * 4 1 A
97 * 6 1 B
98 * 8 1 B
99 * 10 1 A
100 *
101 * where the first column is the time, the second column is the thread id,
102 * and the third column is the method (actually just the class name). The
103 * number of spaces between the 2nd and 3rd columns is the indentation and
104 * determines the call stack. Each called method must be indented by one
105 * more space. In the example above, A is called at time 4, A calls B at
106 * time 6, B returns at time 8, and A returns at time 10. Thread 1 is the
107 * only thread that is running.
108 *
109 * An alternative file format leaves out the first two columns:
110 * A
111 * B
112 * B
113 * A
114 *
115 * In this file format, the thread id is always 1, and the time starts at
116 * 2 and increments by 2 for each line.
117 */
118 void parseInputFile(const char *inputFileName)
119 {
120 unsigned int time = 0, threadId;
121 int len;
122 int linenum = 0;
123 int nextRecord = 0;
124 int indentLevel = 0;
125 stack *callStack;
126
127 FILE *inputFp = fopen(inputFileName, "r");
128 if (inputFp == NULL) {
129 perror(inputFileName);
130 exit(1);
131 }
132
133 /* Count the number of lines in the buffer */
134 int numRecords = 0;
135 int maxThreadId = 1;
136 int maxFrames = 0;
137 char *indentEnd;
138 while (fgets(buf, BUF_SIZE, inputFp)) {
139 char *cp = buf;
140 if (*cp == '#')
141 continue;
142 numRecords += 1;
143 if (isdigit(*cp)) {
144 int time = strtoul(cp, &cp, 0);
145 while (isspace(*cp))
146 cp += 1;
147 int threadId = strtoul(cp, &cp, 0);
148 if (maxThreadId < threadId)
149 maxThreadId = threadId;
150 }
151 indentEnd = cp;
152 while (isspace(*indentEnd))
153 indentEnd += 1;
154 if (indentEnd - cp + 1 > maxFrames)
155 maxFrames = indentEnd - cp + 1;
156 }
157 int numThreads = maxThreadId + 1;
158
159 /* Add space for a sentinel record at the end */
160 numRecords += 1;
161 records = (dataRecord *) malloc(sizeof(dataRecord) * numRecords);
162 callStack = (stack *) malloc(sizeof(stack) * numThreads);
163 int ii;
164 for (ii = 0; ii < numThreads; ++ii) {
165 callStack[ii].frames = NULL;
166 callStack[ii].indentLevel = 0;
167 }
168
169 rewind(inputFp);
170 while (fgets(buf, BUF_SIZE, inputFp)) {
171 int indent;
172 int action;
173 char *save_cp;
174
175 linenum += 1;
176 char *cp = buf;
177
178 /* Skip lines that start with '#' */
179 if (*cp == '#')
180 continue;
181
182 /* Get time and thread id */
183 if (!isdigit(*cp)) {
184 /* If the line does not begin with a digit, then fill in
185 * default values for the time and threadId.
186 */
187 time += 2;
188 threadId = 1;
189 } else {
190 time = strtoul(cp, &cp, 0);
191 while (isspace(*cp))
192 cp += 1;
193 threadId = strtoul(cp, &cp, 0);
194 cp += 1;
195 }
196
197 // Allocate space for the thread stack, if necessary
198 if (callStack[threadId].frames == NULL) {
199 dataRecord **stk;
200 stk = (dataRecord **) malloc(sizeof(dataRecord *) * maxFrames);
201 callStack[threadId].frames = stk;
202 }
203 indentLevel = callStack[threadId].indentLevel;
204
205 save_cp = cp;
206 while (isspace(*cp)) {
207 cp += 1;
208 }
209 indent = cp - save_cp + 1;
210 records[nextRecord].time = time;
211 records[nextRecord].threadId = threadId;
212
213 save_cp = cp;
214 while (*cp != '\n')
215 cp += 1;
216
217 /* Remove trailing spaces */
218 cp -= 1;
219 while (isspace(*cp))
220 cp -= 1;
221 cp += 1;
222 len = cp - save_cp;
223 records[nextRecord].fullName = strndup(save_cp, len);
224
225 /* Parse the name to support "class.method signature" */
226 records[nextRecord].className = NULL;
227 records[nextRecord].methodName = NULL;
228 records[nextRecord].signature = NULL;
229 cp = strchr(save_cp, '.');
230 if (cp) {
231 len = cp - save_cp;
232 if (len > 0)
233 records[nextRecord].className = strndup(save_cp, len);
234 save_cp = cp + 1;
235 cp = strchr(save_cp, ' ');
236 if (cp == NULL)
237 cp = strchr(save_cp, '\n');
238 if (cp && cp > save_cp) {
239 len = cp - save_cp;
240 records[nextRecord].methodName = strndup(save_cp, len);
241 save_cp = cp + 1;
242 cp = strchr(save_cp, ' ');
243 if (cp == NULL)
244 cp = strchr(save_cp, '\n');
245 if (cp && cp > save_cp) {
246 len = cp - save_cp;
247 records[nextRecord].signature = strndup(save_cp, len);
248 }
249 }
250 }
251
252 if (verbose) {
253 printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf);
254 }
255
256 action = 0;
257 if (indent == indentLevel + 1) { // Entering a method
258 if (verbose)
259 printf(" Entering %s\n", records[nextRecord].fullName);
260 callStack[threadId].frames[indentLevel] = &records[nextRecord];
261 } else if (indent == indentLevel) { // Exiting a method
262 // Exiting method must be currently on top of stack (unless stack is empty)
263 if (callStack[threadId].frames[indentLevel - 1] == NULL) {
264 if (verbose)
265 printf(" Exiting %s (past bottom of stack)\n", records[nextRecord].fullName);
266 callStack[threadId].frames[indentLevel - 1] = &records[nextRecord];
267 action = 1;
268 } else {
269 if (indentLevel < 1) {
270 fprintf(stderr, "Error: line %d: %s", linenum, buf);
271 fprintf(stderr, " expected positive (>0) indentation, found %d\n",
272 indent);
273 exit(1);
274 }
275 char *name = callStack[threadId].frames[indentLevel - 1]->fullName;
276 if (strcmp(name, records[nextRecord].fullName) == 0) {
277 if (verbose)
278 printf(" Exiting %s\n", name);
279 action = 1;
280 } else { // exiting method doesn't match stack's top method
281 fprintf(stderr, "Error: line %d: %s", linenum, buf);
282 fprintf(stderr, " expected exit from %s\n",
283 callStack[threadId].frames[indentLevel - 1]->fullName);
284 exit(1);
285 }
286 }
287 } else {
288 if (nextRecord != 0) {
289 fprintf(stderr, "Error: line %d: %s", linenum, buf);
290 fprintf(stderr, " expected indentation %d [+1], found %d\n",
291 indentLevel, indent);
292 exit(1);
293 }
294
295 if (verbose) {
296 printf(" Nonzero indent at first record\n");
297 printf(" Entering %s\n", records[nextRecord].fullName);
298 }
299
300 // This is the first line of data, so we allow a larger
301 // initial indent. This allows us to test popping off more
302 // frames than we entered.
303 indentLevel = indent - 1;
304 callStack[threadId].frames[indentLevel] = &records[nextRecord];
305 }
306
307 if (action == 0)
308 indentLevel += 1;
309 else
310 indentLevel -= 1;
311 records[nextRecord].action = action;
312 callStack[threadId].indentLevel = indentLevel;
313
314 nextRecord += 1;
315 }
316
317 /* Mark the last record with a sentinel */
318 memset(&records[nextRecord], 0, sizeof(dataRecord));
319 }
320
321
322 /*
323 * Write values to the binary data file.
324 */
325 void write2LE(FILE* fp, unsigned short val)
326 {
327 putc(val & 0xff, fp);
328 putc(val >> 8, fp);
329 }
330
331 void write4LE(FILE* fp, unsigned int val)
332 {
333 putc(val & 0xff, fp);
334 putc((val >> 8) & 0xff, fp);
335 putc((val >> 16) & 0xff, fp);
336 putc((val >> 24) & 0xff, fp);
337 }
338
339 void write8LE(FILE* fp, unsigned long long val)
340 {
341 putc(val & 0xff, fp);
342 putc((val >> 8) & 0xff, fp);
343 putc((val >> 16) & 0xff, fp);
344 putc((val >> 24) & 0xff, fp);
345 putc((val >> 32) & 0xff, fp);
346 putc((val >> 40) & 0xff, fp);
347 putc((val >> 48) & 0xff, fp);
348 putc((val >> 56) & 0xff, fp);
349 }
350
351 void writeDataRecord(FILE *dataFp, int threadId, unsigned int methodVal,
352 unsigned int elapsedTime)
353 {
354 if (versionNumber == 1)
355 putc(threadId, dataFp);
356 else
357 write2LE(dataFp, threadId);
358 write4LE(dataFp, methodVal);
359 write4LE(dataFp, elapsedTime);
360 }
361
362 void writeDataHeader(FILE *dataFp)
363 {
364 struct timeval tv;
365 struct timezone tz;
366
367 gettimeofday(&tv, &tz);
368 unsigned long long startTime = tv.tv_sec;
369 startTime = (startTime << 32) | tv.tv_usec;
370 header.version = versionNumber;
371 write4LE(dataFp, header.magic);
372 write2LE(dataFp, header.version);
373 write2LE(dataFp, header.offsetToData);
374 write8LE(dataFp, startTime);
375 }
376
377 void writeKeyMethods(FILE *keyFp)
378 {
379 dataRecord *pRecord, *pNext;
380 char *methodStr = "*methods\n";
381 fwrite(methodStr, strlen(methodStr), 1, keyFp);
382
383 /* Assign method ids in multiples of 4 */
384 unsigned int methodId = 0;
385 for (pRecord = records; pRecord->fullName; ++pRecord) {
386 if (pRecord->methodId)
387 continue;
388 unsigned int id = ++methodId << 2;
389 pRecord->methodId = id;
390
391 /* Assign this id to all the other records that have the
392 * same name.
393 */
394 for (pNext = pRecord + 1; pNext->fullName; ++pNext) {
395 if (pNext->methodId)
396 continue;
397 if (strcmp(pRecord->fullName, pNext->fullName) == 0)
398 pNext->methodId = id;
399 }
400 if (pRecord->className == NULL || pRecord->methodName == NULL) {
401 fprintf(keyFp, "%#x %s m ()\n",
402 pRecord->methodId, pRecord->fullName);
403 } else if (pRecord->signature == NULL) {
404 fprintf(keyFp, "%#x %s %s ()\n",
405 pRecord->methodId, pRecord->className,
406 pRecord->methodName);
407 } else {
408 fprintf(keyFp, "%#x %s %s %s\n",
409 pRecord->methodId, pRecord->className,
410 pRecord->methodName, pRecord->signature);
411 }
412 }
413 }
414
415 void writeKeys(FILE *keyFp)
416 {
417 fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef);
418 fwrite(keyThreads, strlen(keyThreads), 1, keyFp);
419 writeKeyMethods(keyFp);
420 fwrite(keyEnd, strlen(keyEnd), 1, keyFp);
421 }
422
423 void writeDataRecords(FILE *dataFp)
424 {
425 dataRecord *pRecord;
426
427 for (pRecord = records; pRecord->fullName; ++pRecord) {
428 unsigned int val = METHOD_COMBINE(pRecord->methodId, pRecord->action);
429 writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time);
430 }
431 }
432
433 void writeTrace(const char* traceFileName)
434 {
435 FILE *fp = fopen(traceFileName, "w");
436 if (fp == NULL) {
437 perror(traceFileName);
438 exit(1);
439 }
440 writeKeys(fp);
441 writeDataHeader(fp);
442 writeDataRecords(fp);
443 fclose(fp);
444 }
445
446 int parseOptions(int argc, char **argv)
447 {
448 int err = 0;
449 while (1) {
450 int opt = getopt(argc, argv, "v:d");
451 if (opt == -1)
452 break;
453 switch (opt) {
454 case 'v':
455 versionNumber = strtoul(optarg, NULL, 0);
456 if (versionNumber != 1 && versionNumber != 2) {
457 fprintf(stderr, "Error: version number (%d) must be 1 or 2\n",
458 versionNumber);
459 err = 1;
460 }
461 break;
462 case 'd':
463 verbose = 1;
464 break;
465 default:
466 err = 1;
467 break;
468 }
469 }
470 return err;
471 }
472
473 int main(int argc, char** argv)
474 {
475 char *inputFile;
476 char *traceFileName = NULL;
477 int len;
478
479 if (parseOptions(argc, argv) || argc - optind != 2) {
480 fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n",
481 argv[0]);
482 exit(1);
483 }
484
485 inputFile = argv[optind++];
486 parseInputFile(inputFile);
487 traceFileName = argv[optind++];
488
489 writeTrace(traceFileName);
490
491 return 0;
492 }
+0
-43
tools/dmtracedump/Profile.h less more
0 /*
1 * Copyright (C) 2008 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /*
17 * Android's method call profiling goodies.
18 */
19 #ifndef DALVIK_PROFILE_H_
20 #define DALVIK_PROFILE_H_
21
22 /*
23 * Enumeration for the two "action" bits.
24 */
25 enum {
26 METHOD_TRACE_ENTER = 0x00, // method entry
27 METHOD_TRACE_EXIT = 0x01, // method exit
28 METHOD_TRACE_UNROLL = 0x02, // method exited by exception unrolling
29 // 0x03 currently unused
30 };
31
32 #define TOKEN_CHAR '*'
33
34 /*
35 * Common definitions, shared with the dump tool.
36 */
37 #define METHOD_ACTION_MASK 0x03 /* two bits */
38 #define METHOD_ID(_method) ((_method) & (~METHOD_ACTION_MASK))
39 #define METHOD_ACTION(_method) (((unsigned int)(_method)) & METHOD_ACTION_MASK)
40 #define METHOD_COMBINE(_method, _action) ((_method) | (_action))
41
42 #endif // DALVIK_PROFILE_H_
+0
-2910
tools/dmtracedump/TraceDump.c less more
0 /*
1 * Copyright (C) 2006 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /*
17 * Process dmtrace output.
18 *
19 * This is the wrong way to go about it -- C is a clumsy language for
20 * shuffling data around. It'll do for a first pass.
21 */
22 #define NOT_VM
23 #include "Profile.h" // from VM header
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <inttypes.h>
30 #include <time.h>
31 #include <errno.h>
32 #include <assert.h>
33
34 /* Version number in the key file.
35 * Version 1 uses one byte for the thread id.
36 * Version 2 uses two bytes for the thread ids.
37 * Version 3 encodes the record size and adds an optional extra timestamp field.
38 */
39 int versionNumber;
40
41 /* arbitrarily limit indentation */
42 #define MAX_STACK_DEPTH 10000
43
44 /* thread list in key file is not reliable, so just max out */
45 #define MAX_THREADS 32768
46
47 /* Size of temporary buffers for escaping html strings */
48 #define HTML_BUFSIZE 10240
49
50 char *htmlHeader =
51 "<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n"
52 "<script langugage=\"javascript\">\n"
53 "function toggle(item) {\n"
54 " obj=document.getElementById(item);\n"
55 " visible=(obj.style.display!=\"none\" && obj.style.display!=\"\");\n"
56 " key=document.getElementById(\"x\" + item);\n"
57 " if (visible) {\n"
58 " obj.style.display=\"none\";\n"
59 " key.innerHTML=\"+\";\n"
60 " } else {\n"
61 " obj.style.display=\"block\";\n"
62 " key.innerHTML=\"-\";\n"
63 " }\n"
64 "}\n"
65 "function onMouseOver(obj) {\n"
66 " obj.style.background=\"lightblue\";\n"
67 "}\n"
68 "function onMouseOut(obj) {\n"
69 " obj.style.background=\"white\";\n"
70 "}\n"
71 "</script>\n"
72 "<style type=\"text/css\">\n"
73 "div { font-family: courier; font-size: 13 }\n"
74 "div.parent { margin-left: 15; display: none }\n"
75 "div.leaf { margin-left: 10 }\n"
76 "div.header { margin-left: 10 }\n"
77 "div.link { margin-left: 10; cursor: move }\n"
78 "span.parent { padding-right: 10; }\n"
79 "span.leaf { padding-right: 10; }\n"
80 "a img { border: 0;}\n"
81 "table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}\n"
82 "a { text-decoration: none; }\n"
83 "a:hover { text-decoration: underline; }\n"
84 "table.sortable th, table.sortable td { text-align: left;}"
85 "table.sortable tr.odd td { background-color: #ddd; }\n"
86 "table.sortable tr.even td { background-color: #fff; }\n"
87 "</style>\n"
88 "</head><body>\n\n";
89
90 char *htmlFooter = "\n</body>\n</html>\n";
91 char *profileSeparator =
92 "======================================================================";
93
94 const char* tableHeader =
95 "<table class='sortable' id='%s'><tr>\n"
96 "<th>Method</th>\n"
97 "<th>Run 1 (us)</th>\n"
98 "<th>Run 2 (us)</th>\n"
99 "<th>Diff (us)</th>\n"
100 "<th>Diff (%%)</th>\n"
101 "<th>1: # calls</th>\n"
102 "<th>2: # calls</th>\n"
103 "</tr>\n";
104
105 const char* tableHeaderMissing =
106 "<table class='sortable' id='%s'>\n"
107 "<th>Method</th>\n"
108 "<th>Exclusive</th>\n"
109 "<th>Inclusive</th>\n"
110 "<th># calls</th>\n";
111
112 #define GRAPH_LABEL_VISITED 0x0001
113 #define GRAPH_NODE_VISITED 0x0002
114
115 /*
116 * Values from the header of the data file.
117 */
118 typedef struct DataHeader {
119 unsigned int magic;
120 short version;
121 short offsetToData;
122 long long startWhen;
123 short recordSize;
124 } DataHeader;
125
126 /*
127 * Entry from the thread list.
128 */
129 typedef struct ThreadEntry {
130 int threadId;
131 const char* threadName;
132 } ThreadEntry;
133
134 struct MethodEntry;
135 typedef struct TimedMethod {
136 struct TimedMethod *next;
137 uint64_t elapsedInclusive;
138 int numCalls;
139 struct MethodEntry *method;
140 } TimedMethod;
141
142 typedef struct ClassEntry {
143 const char *className;
144 uint64_t elapsedExclusive;
145 int numMethods;
146 struct MethodEntry **methods; /* list of methods in this class */
147 int numCalls[2]; /* 0=normal, 1=recursive */
148 } ClassEntry;
149
150 typedef struct UniqueMethodEntry {
151 uint64_t elapsedExclusive;
152 int numMethods;
153 struct MethodEntry **methods; /* list of methods with same name */
154 int numCalls[2]; /* 0=normal, 1=recursive */
155 } UniqueMethodEntry;
156
157 /*
158 * Entry from the method list.
159 */
160 typedef struct MethodEntry {
161 int64_t methodId;
162 const char* className;
163 const char* methodName;
164 const char* signature;
165 const char* fileName;
166 int lineNum;
167 uint64_t elapsedExclusive;
168 uint64_t elapsedInclusive;
169 uint64_t topExclusive; /* non-recursive exclusive time */
170 uint64_t recursiveInclusive;
171 struct TimedMethod *parents[2]; /* 0=normal, 1=recursive */
172 struct TimedMethod *children[2]; /* 0=normal, 1=recursive */
173 int numCalls[2]; /* 0=normal, 1=recursive */
174 int index; /* used after sorting to number methods */
175 int recursiveEntries; /* number of entries on the stack */
176 int graphState; /* used when graphing to see if this method has been visited before */
177 } MethodEntry;
178
179 /*
180 * The parsed contents of the key file.
181 */
182 typedef struct DataKeys {
183 char* fileData; /* contents of the entire file */
184 long fileLen;
185 int numThreads;
186 ThreadEntry* threads;
187 int numMethods;
188 MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */
189 } DataKeys;
190
191 #define TOPLEVEL_INDEX 0
192 #define UNKNOWN_INDEX 1
193
194 typedef struct StackEntry {
195 MethodEntry *method;
196 uint64_t entryTime;
197 } StackEntry;
198
199 typedef struct CallStack {
200 int top;
201 StackEntry calls[MAX_STACK_DEPTH];
202 uint64_t lastEventTime;
203 uint64_t threadStartTime;
204 } CallStack;
205
206 typedef struct DiffEntry {
207 MethodEntry* method1;
208 MethodEntry* method2;
209 int64_t differenceExclusive;
210 int64_t differenceInclusive;
211 double differenceExclusivePercentage;
212 double differenceInclusivePercentage;
213 } DiffEntry;
214
215 // Global options
216 typedef struct Options {
217 const char* traceFileName;
218 const char* diffFileName;
219 const char* graphFileName;
220 int keepDotFile;
221 int dump;
222 int outputHtml;
223 const char* sortableUrl;
224 int threshold;
225 } Options;
226
227 typedef struct TraceData {
228 int numClasses;
229 ClassEntry *classes;
230 CallStack *stacks[MAX_THREADS];
231 int depth[MAX_THREADS];
232 int numUniqueMethods;
233 UniqueMethodEntry *uniqueMethods;
234 } TraceData;
235
236 static Options gOptions;
237
238 /* Escapes characters in the source string that are html special entities.
239 * The escaped string is written to "dest" which must be large enough to
240 * hold the result. A pointer to "dest" is returned. The characters and
241 * their corresponding escape sequences are:
242 * '<' &lt;
243 * '>' &gt;
244 * '&' &amp;
245 */
246 char *htmlEscape(const char *src, char *dest, int len)
247 {
248 char *destStart = dest;
249
250 if (src == NULL)
251 return NULL;
252
253 int nbytes = 0;
254 while (*src) {
255 if (*src == '<') {
256 nbytes += 4;
257 if (nbytes >= len)
258 break;
259 *dest++ = '&';
260 *dest++ = 'l';
261 *dest++ = 't';
262 *dest++ = ';';
263 } else if (*src == '>') {
264 nbytes += 4;
265 if (nbytes >= len)
266 break;
267 *dest++ = '&';
268 *dest++ = 'g';
269 *dest++ = 't';
270 *dest++ = ';';
271 } else if (*src == '&') {
272 nbytes += 5;
273 if (nbytes >= len)
274 break;
275 *dest++ = '&';
276 *dest++ = 'a';
277 *dest++ = 'm';
278 *dest++ = 'p';
279 *dest++ = ';';
280 } else {
281 nbytes += 1;
282 if (nbytes >= len)
283 break;
284 *dest++ = *src;
285 }
286 src += 1;
287 }
288 if (nbytes >= len) {
289 fprintf(stderr, "htmlEscape(): buffer overflow\n");
290 exit(1);
291 }
292 *dest = 0;
293
294 return destStart;
295 }
296
297 /* Initializes a MethodEntry
298 */
299 void initMethodEntry(MethodEntry *method, int64_t methodId,
300 const char *className, const char *methodName,
301 const char *signature, const char* fileName,
302 const char* lineNumStr)
303 {
304 method->methodId = methodId;
305 method->className = className;
306 method->methodName = methodName;
307 method->signature = signature;
308 method->fileName = fileName;
309 method->lineNum = (lineNumStr != NULL) ? atoi(lineNumStr) : -1;
310 method->elapsedExclusive = 0;
311 method->elapsedInclusive = 0;
312 method->topExclusive = 0;
313 method->recursiveInclusive = 0;
314 method->parents[0] = NULL;
315 method->parents[1] = NULL;
316 method->children[0] = NULL;
317 method->children[1] = NULL;
318 method->numCalls[0] = 0;
319 method->numCalls[1] = 0;
320 method->index = 0;
321 method->recursiveEntries = 0;
322 }
323
324 /*
325 * This comparison function is called from qsort() to sort
326 * methods into decreasing order of exclusive elapsed time.
327 */
328 int compareElapsedExclusive(const void *a, const void *b) {
329 uint64_t elapsed1, elapsed2;
330 int result;
331
332 const MethodEntry *methodA = *(const MethodEntry**)a;
333 const MethodEntry *methodB = *(const MethodEntry**)b;
334 elapsed1 = methodA->elapsedExclusive;
335 elapsed2 = methodB->elapsedExclusive;
336 if (elapsed1 < elapsed2)
337 return 1;
338 if (elapsed1 > elapsed2)
339 return -1;
340
341 /* If the elapsed times of two methods are equal, then sort them
342 * into alphabetical order.
343 */
344 result = strcmp(methodA->className, methodB->className);
345 if (result == 0) {
346 if (methodA->methodName == NULL || methodB->methodName == NULL) {
347 int64_t idA = methodA->methodId;
348 int64_t idB = methodB->methodId;
349 if (idA < idB)
350 return -1;
351 if (idA > idB)
352 return 1;
353 return 0;
354 }
355 result = strcmp(methodA->methodName, methodB->methodName);
356 if (result == 0)
357 result = strcmp(methodA->signature, methodB->signature);
358 }
359 return result;
360 }
361
362 /*
363 * This comparison function is called from qsort() to sort
364 * methods into decreasing order of inclusive elapsed time.
365 */
366 int compareElapsedInclusive(const void *a, const void *b) {
367 const MethodEntry *methodA, *methodB;
368 uint64_t elapsed1, elapsed2;
369 int result;
370
371 methodA = *(MethodEntry const **)a;
372 methodB = *(MethodEntry const **)b;
373 elapsed1 = methodA->elapsedInclusive;
374 elapsed2 = methodB->elapsedInclusive;
375 if (elapsed1 < elapsed2)
376 return 1;
377 if (elapsed1 > elapsed2)
378 return -1;
379
380 /* If the elapsed times of two methods are equal, then sort them
381 * into alphabetical order.
382 */
383 result = strcmp(methodA->className, methodB->className);
384 if (result == 0) {
385 if (methodA->methodName == NULL || methodB->methodName == NULL) {
386 int64_t idA = methodA->methodId;
387 int64_t idB = methodB->methodId;
388 if (idA < idB)
389 return -1;
390 if (idA > idB)
391 return 1;
392 return 0;
393 }
394 result = strcmp(methodA->methodName, methodB->methodName);
395 if (result == 0)
396 result = strcmp(methodA->signature, methodB->signature);
397 }
398 return result;
399 }
400
401 /*
402 * This comparison function is called from qsort() to sort
403 * TimedMethods into decreasing order of inclusive elapsed time.
404 */
405 int compareTimedMethod(const void *a, const void *b) {
406 const TimedMethod *timedA, *timedB;
407 uint64_t elapsed1, elapsed2;
408 int result;
409
410 timedA = (TimedMethod const *)a;
411 timedB = (TimedMethod const *)b;
412 elapsed1 = timedA->elapsedInclusive;
413 elapsed2 = timedB->elapsedInclusive;
414 if (elapsed1 < elapsed2)
415 return 1;
416 if (elapsed1 > elapsed2)
417 return -1;
418
419 /* If the elapsed times of two methods are equal, then sort them
420 * into alphabetical order.
421 */
422 MethodEntry *methodA = timedA->method;
423 MethodEntry *methodB = timedB->method;
424 result = strcmp(methodA->className, methodB->className);
425 if (result == 0) {
426 if (methodA->methodName == NULL || methodB->methodName == NULL) {
427 int64_t idA = methodA->methodId;
428 int64_t idB = methodB->methodId;
429 if (idA < idB)
430 return -1;
431 if (idA > idB)
432 return 1;
433 return 0;
434 }
435 result = strcmp(methodA->methodName, methodB->methodName);
436 if (result == 0)
437 result = strcmp(methodA->signature, methodB->signature);
438 }
439 return result;
440 }
441
442 /*
443 * This comparison function is called from qsort() to sort
444 * MethodEntry pointers into alphabetical order of class names.
445 */
446 int compareClassNames(const void *a, const void *b) {
447 int result;
448
449 const MethodEntry *methodA = *(const MethodEntry**)a;
450 const MethodEntry *methodB = *(const MethodEntry**)b;
451 result = strcmp(methodA->className, methodB->className);
452 if (result == 0) {
453 int64_t idA = methodA->methodId;
454 int64_t idB = methodB->methodId;
455 if (idA < idB)
456 return -1;
457 if (idA > idB)
458 return 1;
459 return 0;
460 }
461 return result;
462 }
463
464 /*
465 * This comparison function is called from qsort() to sort
466 * classes into decreasing order of exclusive elapsed time.
467 */
468 int compareClassExclusive(const void *a, const void *b) {
469 uint64_t elapsed1, elapsed2;
470 int result;
471
472 const ClassEntry *classA = *(const ClassEntry**)a;
473 const ClassEntry *classB = *(const ClassEntry**)b;
474 elapsed1 = classA->elapsedExclusive;
475 elapsed2 = classB->elapsedExclusive;
476 if (elapsed1 < elapsed2)
477 return 1;
478 if (elapsed1 > elapsed2)
479 return -1;
480
481 /* If the elapsed times of two classs are equal, then sort them
482 * into alphabetical order.
483 */
484 result = strcmp(classA->className, classB->className);
485 if (result == 0) {
486 /* Break ties with the first method id. This is probably not
487 * needed.
488 */
489 int64_t idA = classA->methods[0]->methodId;
490 int64_t idB = classB->methods[0]->methodId;
491 if (idA < idB)
492 return -1;
493 if (idA > idB)
494 return 1;
495 return 0;
496 }
497 return result;
498 }
499
500 /*
501 * This comparison function is called from qsort() to sort
502 * MethodEntry pointers into alphabetical order by method name,
503 * then by class name.
504 */
505 int compareMethodNames(const void *a, const void *b) {
506 int result;
507
508 const MethodEntry *methodA = *(const MethodEntry**)a;
509 const MethodEntry *methodB = *(const MethodEntry**)b;
510 if (methodA->methodName == NULL || methodB->methodName == NULL) {
511 return compareClassNames(a, b);
512 }
513 result = strcmp(methodA->methodName, methodB->methodName);
514 if (result == 0) {
515 result = strcmp(methodA->className, methodB->className);
516 if (result == 0) {
517 int64_t idA = methodA->methodId;
518 int64_t idB = methodB->methodId;
519 if (idA < idB)
520 return -1;
521 if (idA > idB)
522 return 1;
523 return 0;
524 }
525 }
526 return result;
527 }
528
529 /*
530 * This comparison function is called from qsort() to sort
531 * unique methods into decreasing order of exclusive elapsed time.
532 */
533 int compareUniqueExclusive(const void *a, const void *b) {
534 uint64_t elapsed1, elapsed2;
535 int result;
536
537 const UniqueMethodEntry *uniqueA = *(const UniqueMethodEntry**)a;
538 const UniqueMethodEntry *uniqueB = *(const UniqueMethodEntry**)b;
539 elapsed1 = uniqueA->elapsedExclusive;
540 elapsed2 = uniqueB->elapsedExclusive;
541 if (elapsed1 < elapsed2)
542 return 1;
543 if (elapsed1 > elapsed2)
544 return -1;
545
546 /* If the elapsed times of two methods are equal, then sort them
547 * into alphabetical order.
548 */
549 result = strcmp(uniqueA->methods[0]->className,
550 uniqueB->methods[0]->className);
551 if (result == 0) {
552 int64_t idA = uniqueA->methods[0]->methodId;
553 int64_t idB = uniqueB->methods[0]->methodId;
554 if (idA < idB)
555 return -1;
556 if (idA > idB)
557 return 1;
558 return 0;
559 }
560 return result;
561 }
562
563 /*
564 * Free a DataKeys struct.
565 */
566 void freeDataKeys(DataKeys* pKeys)
567 {
568 if (pKeys == NULL)
569 return;
570
571 free(pKeys->fileData);
572 free(pKeys->threads);
573 free(pKeys->methods);
574 free(pKeys);
575 }
576
577 /*
578 * Find the offset to the next occurrence of the specified character.
579 *
580 * "data" should point somewhere within the current line. "len" is the
581 * number of bytes left in the buffer.
582 *
583 * Returns -1 if we hit the end of the buffer.
584 */
585 int findNextChar(const char* data, int len, char lookFor)
586 {
587 const char* start = data;
588
589 while (len > 0) {
590 if (*data == lookFor)
591 return data - start;
592
593 data++;
594 len--;
595 }
596
597 return -1;
598 }
599
600 /*
601 * Count the number of lines until the next token.
602 *
603 * Returns -1 if none found before EOF.
604 */
605 int countLinesToToken(const char* data, int len)
606 {
607 int count = 0;
608 int next;
609
610 while (*data != TOKEN_CHAR) {
611 next = findNextChar(data, len, '\n');
612 if (next < 0)
613 return -1;
614 count++;
615 data += next+1;
616 len -= next+1;
617 }
618
619 return count;
620 }
621
622 /*
623 * Make sure we're at the start of the right section.
624 *
625 * Returns the length of the token line, or -1 if something is wrong.
626 */
627 int checkToken(const char* data, int len, const char* cmpStr)
628 {
629 int cmpLen = strlen(cmpStr);
630 int next;
631
632 if (*data != TOKEN_CHAR) {
633 fprintf(stderr,
634 "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data);
635 return -1;
636 }
637
638 next = findNextChar(data, len, '\n');
639 if (next < cmpLen+1)
640 return -1;
641
642 if (strncmp(data+1, cmpStr, cmpLen) != 0) {
643 fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data+1);
644 return -1;
645 }
646
647 return next+1;
648 }
649
650 /*
651 * Parse the "*version" section.
652 */
653 long parseVersion(DataKeys* pKeys, long offset, int verbose)
654 {
655 char* data;
656 char* dataEnd;
657 int i, count, next;
658
659 if (offset < 0)
660 return -1;
661
662 data = pKeys->fileData + offset;
663 dataEnd = pKeys->fileData + pKeys->fileLen;
664 next = checkToken(data, dataEnd - data, "version");
665 if (next <= 0)
666 return -1;
667
668 data += next;
669
670 /*
671 * Count the number of items in the "version" section.
672 */
673 count = countLinesToToken(data, dataEnd - data);
674 if (count <= 0) {
675 fprintf(stderr,
676 "ERROR: failed while reading version (found %d)\n", count);
677 return -1;
678 }
679
680 /* find the end of the line */
681 next = findNextChar(data, dataEnd - data, '\n');
682 if (next < 0)
683 return -1;
684
685 data[next] = '\0';
686 versionNumber = strtoul(data, NULL, 0);
687 if (verbose)
688 printf("VERSION: %d\n", versionNumber);
689
690 data += next+1;
691
692 /* skip over the rest of the stuff, which is "name=value" lines */
693 for (i = 1; i < count; i++) {
694 next = findNextChar(data, dataEnd - data, '\n');
695 if (next < 0)
696 return -1;
697 //data[next] = '\0';
698 //printf("IGNORING: '%s'\n", data);
699 data += next+1;
700 }
701
702 return data - pKeys->fileData;
703 }
704
705 /*
706 * Parse the "*threads" section.
707 */
708 long parseThreads(DataKeys* pKeys, long offset)
709 {
710 char* data;
711 char* dataEnd;
712 int i, next, tab, count;
713
714 if (offset < 0)
715 return -1;
716
717 data = pKeys->fileData + offset;
718 dataEnd = pKeys->fileData + pKeys->fileLen;
719 next = checkToken(data, dataEnd - data, "threads");
720
721 data += next;
722
723 /*
724 * Count the number of thread entries (one per line).
725 */
726 count = countLinesToToken(data, dataEnd - data);
727 if (count <= 0) {
728 fprintf(stderr,
729 "ERROR: failed while reading threads (found %d)\n", count);
730 return -1;
731 }
732
733 //printf("+++ found %d threads\n", count);
734 pKeys->threads = (ThreadEntry*) malloc(sizeof(ThreadEntry) * count);
735 if (pKeys->threads == NULL)
736 return -1;
737
738 /*
739 * Extract all entries.
740 */
741 for (i = 0; i < count; i++) {
742 next = findNextChar(data, dataEnd - data, '\n');
743 assert(next > 0);
744 data[next] = '\0';
745
746 tab = findNextChar(data, next, '\t');
747 data[tab] = '\0';
748
749 pKeys->threads[i].threadId = atoi(data);
750 pKeys->threads[i].threadName = data + tab +1;
751
752 data += next+1;
753 }
754
755 pKeys->numThreads = count;
756 return data - pKeys->fileData;
757 }
758
759 /*
760 * Parse the "*methods" section.
761 */
762 long parseMethods(DataKeys* pKeys, long offset)
763 {
764 char* data;
765 char* dataEnd;
766 int i, next, count;
767
768 if (offset < 0)
769 return -1;
770
771 data = pKeys->fileData + offset;
772 dataEnd = pKeys->fileData + pKeys->fileLen;
773 next = checkToken(data, dataEnd - data, "methods");
774 if (next < 0)
775 return -1;
776
777 data += next;
778
779 /*
780 * Count the number of method entries (one per line).
781 */
782 count = countLinesToToken(data, dataEnd - data);
783 if (count <= 0) {
784 fprintf(stderr,
785 "ERROR: failed while reading methods (found %d)\n", count);
786 return -1;
787 }
788
789 /* Reserve an extra method at location 0 for the "toplevel" method,
790 * and another extra method for all other "unknown" methods.
791 */
792 count += 2;
793 pKeys->methods = (MethodEntry*) malloc(sizeof(MethodEntry) * count);
794 if (pKeys->methods == NULL)
795 return -1;
796 initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], -2, "(toplevel)",
797 NULL, NULL, NULL, NULL);
798 initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], -1, "(unknown)",
799 NULL, NULL, NULL, NULL);
800
801 /*
802 * Extract all entries, starting with index 2.
803 */
804 for (i = UNKNOWN_INDEX + 1; i < count; i++) {
805 int tab1, tab2, tab3, tab4, tab5;
806 int64_t id;
807 char* endptr;
808
809 next = findNextChar(data, dataEnd - data, '\n');
810 assert(next > 0);
811 data[next] = '\0';
812
813 tab1 = findNextChar(data, next, '\t');
814 tab2 = findNextChar(data+(tab1+1), next-(tab1+1), '\t');
815 tab3 = findNextChar(data+(tab1+tab2+2), next-(tab1+tab2+2), '\t');
816 tab4 = findNextChar(data+(tab1+tab2+tab3+3),
817 next-(tab1+tab2+tab3+3), '\t');
818 tab5 = findNextChar(data+(tab1+tab2+tab3+tab4+4),
819 next-(tab1+tab2+tab3+tab4+4), '\t');
820 if (tab1 < 0) {
821 fprintf(stderr, "ERROR: missing field on method line: '%s'\n",
822 data);
823 return -1;
824 }
825 assert(data[tab1] == '\t');
826 data[tab1] = '\0';
827
828 id = strtoul(data, &endptr, 0);
829 if (*endptr != '\0') {
830 fprintf(stderr, "ERROR: bad method ID '%s'\n", data);
831 return -1;
832 }
833
834 // Allow files that specify just a function name, instead of requiring
835 // "class \t method \t signature"
836 if (tab2 > 0 && tab3 > 0) {
837 tab2 += tab1+1;
838 tab3 += tab2+1;
839 assert(data[tab2] == '\t');
840 assert(data[tab3] == '\t');
841 data[tab2] = data[tab3] = '\0';
842
843 // This is starting to get awkward. Allow filename and line #.
844 if (tab4 > 0 && tab5 > 0) {
845 tab4 += tab3+1;
846 tab5 += tab4+1;
847
848 assert(data[tab4] == '\t');
849 assert(data[tab5] == '\t');
850 data[tab4] = data[tab5] = '\0';
851
852 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
853 data + tab2 +1, data + tab3 +1, data + tab4 +1,
854 data + tab5 +1);
855 } else {
856 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
857 data + tab2 +1, data + tab3 +1, NULL, NULL);
858 }
859 } else {
860 initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
861 NULL, NULL, NULL, NULL);
862 }
863
864 data += next+1;
865 }
866
867 pKeys->numMethods = count;
868 return data - pKeys->fileData;
869 }
870
871 /*
872 * Parse the "*end" section.
873 */
874 long parseEnd(DataKeys* pKeys, long offset)
875 {
876 char* data;
877 char* dataEnd;
878 int next;
879
880 if (offset < 0)
881 return -1;
882
883 data = pKeys->fileData + offset;
884 dataEnd = pKeys->fileData + pKeys->fileLen;
885 next = checkToken(data, dataEnd - data, "end");
886 if (next < 0)
887 return -1;
888
889 data += next;
890
891 return data - pKeys->fileData;
892 }
893
894 /*
895 * Sort the thread list entries.
896 */
897 static int compareThreads(const void* thread1, const void* thread2)
898 {
899 return ((const ThreadEntry*) thread1)->threadId -
900 ((const ThreadEntry*) thread2)->threadId;
901 }
902
903 void sortThreadList(DataKeys* pKeys)
904 {
905 qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]),
906 compareThreads);
907 }
908
909 /*
910 * Sort the method list entries.
911 */
912 static int compareMethods(const void* meth1, const void* meth2)
913 {
914 int64_t id1, id2;
915
916 id1 = ((const MethodEntry*) meth1)->methodId;
917 id2 = ((const MethodEntry*) meth2)->methodId;
918 if (id1 < id2)
919 return -1;
920 if (id1 > id2)
921 return 1;
922 return 0;
923 }
924
925 void sortMethodList(DataKeys* pKeys)
926 {
927 qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry),
928 compareMethods);
929 }
930
931 /*
932 * Parse the key section, and return a copy of the parsed contents.
933 */
934 DataKeys* parseKeys(FILE *fp, int verbose)
935 {
936 DataKeys* pKeys = NULL;
937 long offset;
938 int i;
939
940 pKeys = (DataKeys*) calloc(1, sizeof(DataKeys));
941 if (pKeys == NULL)
942 goto fail;
943
944 /*
945 * We load the entire file into memory. We do this, rather than memory-
946 * mapping it, because we want to change some whitespace to NULs.
947 */
948 if (fseek(fp, 0L, SEEK_END) != 0) {
949 perror("fseek");
950 goto fail;
951 }
952 pKeys->fileLen = ftell(fp);
953 if (pKeys->fileLen == 0) {
954 fprintf(stderr, "Key file is empty.\n");
955 goto fail;
956 }
957 rewind(fp);
958
959 pKeys->fileData = (char*) malloc(pKeys->fileLen);
960 if (pKeys->fileData == NULL) {
961 fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", pKeys->fileLen);
962 goto fail;
963 }
964
965 if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t) pKeys->fileLen)
966 {
967 fprintf(stderr, "ERROR: unable to read %ld bytes from trace file\n",
968 pKeys->fileLen);
969 goto fail;
970 }
971
972 offset = 0;
973
974 offset = parseVersion(pKeys, offset, verbose);
975 offset = parseThreads(pKeys, offset);
976 offset = parseMethods(pKeys, offset);
977 offset = parseEnd(pKeys, offset);
978 if (offset < 0)
979 goto fail;
980
981 /* Reduce our allocation now that we know where the end of the key section is. */
982 pKeys->fileData = (char *)realloc(pKeys->fileData, offset);
983 pKeys->fileLen = offset;
984 /* Leave fp pointing to the beginning of the data section. */
985 fseek(fp, offset, SEEK_SET);
986
987 sortThreadList(pKeys);
988 sortMethodList(pKeys);
989
990 /*
991 * Dump list of threads.
992 */
993 if (verbose) {
994 printf("Threads (%d):\n", pKeys->numThreads);
995 for (i = 0; i < pKeys->numThreads; i++) {
996 printf("%2d %s\n",
997 pKeys->threads[i].threadId, pKeys->threads[i].threadName);
998 }
999 }
1000
1001 #if 0
1002 /*
1003 * Dump list of methods.
1004 */
1005 if (verbose) {
1006 printf("Methods (%d):\n", pKeys->numMethods);
1007 for (i = 0; i < pKeys->numMethods; i++) {
1008 printf("0x%08x %s : %s : %s\n",
1009 pKeys->methods[i].methodId, pKeys->methods[i].className,
1010 pKeys->methods[i].methodName, pKeys->methods[i].signature);
1011 }
1012 }
1013 #endif
1014
1015 return pKeys;
1016
1017 fail:
1018 freeDataKeys(pKeys);
1019 return NULL;
1020 }
1021
1022
1023 /*
1024 * Read values from the binary data file.
1025 */
1026
1027 /* Make the return value "unsigned int" instead of "unsigned short" so that
1028 * we can detect EOF.
1029 */
1030 unsigned int read2LE(FILE* fp)
1031 {
1032 unsigned int val;
1033
1034 val = getc(fp);
1035 val |= getc(fp) << 8;
1036 return val;
1037 }
1038 unsigned int read4LE(FILE* fp)
1039 {
1040 unsigned int val;
1041
1042 val = getc(fp);
1043 val |= getc(fp) << 8;
1044 val |= getc(fp) << 16;
1045 val |= getc(fp) << 24;
1046 return val;
1047 }
1048 unsigned long long read8LE(FILE* fp)
1049 {
1050 unsigned long long val;
1051
1052 val = getc(fp);
1053 val |= (unsigned long long) getc(fp) << 8;
1054 val |= (unsigned long long) getc(fp) << 16;
1055 val |= (unsigned long long) getc(fp) << 24;
1056 val |= (unsigned long long) getc(fp) << 32;
1057 val |= (unsigned long long) getc(fp) << 40;
1058 val |= (unsigned long long) getc(fp) << 48;
1059 val |= (unsigned long long) getc(fp) << 56;
1060 return val;
1061 }
1062
1063 /*
1064 * Parse the header of the data section.
1065 *
1066 * Returns with the file positioned at the start of the record data.
1067 */
1068 int parseDataHeader(FILE *fp, DataHeader* pHeader)
1069 {
1070 int bytesToRead;
1071
1072 pHeader->magic = read4LE(fp);
1073 pHeader->version = read2LE(fp);
1074 pHeader->offsetToData = read2LE(fp);
1075 pHeader->startWhen = read8LE(fp);
1076 bytesToRead = pHeader->offsetToData - 16;
1077 if (pHeader->version == 1) {
1078 pHeader->recordSize = 9;
1079 } else if (pHeader->version == 2) {
1080 pHeader->recordSize = 10;
1081 } else if (pHeader->version == 3) {
1082 pHeader->recordSize = read2LE(fp);
1083 bytesToRead -= 2;
1084 } else {
1085 fprintf(stderr, "Unsupported trace file version: %d\n", pHeader->version);
1086 return -1;
1087 }
1088
1089 if (fseek(fp, bytesToRead, SEEK_CUR) != 0) {
1090 return -1;
1091 }
1092
1093 return 0;
1094 }
1095
1096 /*
1097 * Look up a method by it's method ID.
1098 *
1099 * Returns NULL if no matching method was found.
1100 */
1101 MethodEntry* lookupMethod(DataKeys* pKeys, int64_t methodId)
1102 {
1103 int hi, lo, mid;
1104 int64_t id;
1105
1106 lo = 0;
1107 hi = pKeys->numMethods - 1;
1108
1109 while (hi >= lo) {
1110 mid = (hi + lo) / 2;
1111
1112 id = pKeys->methods[mid].methodId;
1113 if (id == methodId) /* match */
1114 return &pKeys->methods[mid];
1115 else if (id < methodId) /* too low */
1116 lo = mid + 1;
1117 else /* too high */
1118 hi = mid - 1;
1119 }
1120
1121 return NULL;
1122 }
1123
1124 /*
1125 * Reads the next data record, and assigns the data values to threadId,
1126 * methodVal and elapsedTime. On end-of-file, the threadId, methodVal,
1127 * and elapsedTime are unchanged. Returns 1 on end-of-file, otherwise
1128 * returns 0.
1129 */
1130 int readDataRecord(FILE *dataFp, DataHeader* dataHeader,
1131 int *threadId, unsigned int *methodVal, uint64_t *elapsedTime)
1132 {
1133 int id;
1134 int bytesToRead;
1135
1136 bytesToRead = dataHeader->recordSize;
1137 if (dataHeader->version == 1) {
1138 id = getc(dataFp);
1139 bytesToRead -= 1;
1140 } else {
1141 id = read2LE(dataFp);
1142 bytesToRead -= 2;
1143 }
1144 if (id == EOF)
1145 return 1;
1146 *threadId = id;
1147
1148 *methodVal = read4LE(dataFp);
1149 *elapsedTime = read4LE(dataFp);
1150 bytesToRead -= 8;
1151
1152 while (bytesToRead-- > 0) {
1153 getc(dataFp);
1154 }
1155
1156 if (feof(dataFp)) {
1157 fprintf(stderr, "WARNING: hit EOF mid-record\n");
1158 return 1;
1159 }
1160 return 0;
1161 }
1162
1163 /*
1164 * Read the key file and use it to produce formatted output from the
1165 * data file.
1166 */
1167 void dumpTrace()
1168 {
1169 static const char* actionStr[] = { "ent", "xit", "unr", "???" };
1170 MethodEntry bogusMethod = { 0, "???", "???", "???", "???", -1, 0, 0, 0, 0,
1171 {NULL, NULL}, {NULL, NULL}, {0, 0}, 0, 0, -1 };
1172 char bogusBuf[80];
1173 char spaces[MAX_STACK_DEPTH+1];
1174 FILE* dataFp = NULL;
1175 DataHeader dataHeader;
1176 DataKeys* pKeys = NULL;
1177 int i;
1178 TraceData traceData;
1179
1180 //printf("Dumping '%s' '%s'\n", dataFileName, keyFileName);
1181
1182 memset(spaces, '.', MAX_STACK_DEPTH);
1183 spaces[MAX_STACK_DEPTH] = '\0';
1184
1185 for (i = 0; i < MAX_THREADS; i++)
1186 traceData.depth[i] = 2; // adjust for return from start function
1187
1188 dataFp = fopen(gOptions.traceFileName, "rb");
1189 if (dataFp == NULL)
1190 goto bail;
1191
1192 if ((pKeys = parseKeys(dataFp, 1)) == NULL)
1193 goto bail;
1194
1195 if (parseDataHeader(dataFp, &dataHeader) < 0)
1196 goto bail;
1197
1198 printf("Trace (threadID action usecs class.method signature):\n");
1199
1200 while (1) {
1201 MethodEntry* method;
1202 int threadId;
1203 unsigned int methodVal;
1204 uint64_t elapsedTime;
1205 int action, printDepth;
1206 int64_t methodId, lastEnter = 0;
1207 int mismatch = 0;
1208 char depthNote;
1209
1210 /*
1211 * Extract values from file.
1212 */
1213 if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &elapsedTime))
1214 break;
1215
1216 action = METHOD_ACTION(methodVal);
1217 methodId = METHOD_ID(methodVal);
1218
1219 /*
1220 * Generate a line of output.
1221 */
1222 if (action == METHOD_TRACE_ENTER) {
1223 traceData.depth[threadId]++;
1224 lastEnter = methodId;
1225 } else {
1226 /* quick test for mismatched adjacent enter/exit */
1227 if (lastEnter != 0 && lastEnter != methodId)
1228 mismatch = 1;
1229 }
1230
1231 printDepth = traceData.depth[threadId];
1232 depthNote = ' ';
1233 if (printDepth < 0) {
1234 printDepth = 0;
1235 depthNote = '-';
1236 } else if (printDepth > MAX_STACK_DEPTH) {
1237 printDepth = MAX_STACK_DEPTH;
1238 depthNote = '+';
1239 }
1240
1241 method = lookupMethod(pKeys, methodId);
1242 if (method == NULL) {
1243 method = &bogusMethod;
1244 sprintf(bogusBuf, "methodId: %#" PRIx64 "", methodId);
1245 method->signature = bogusBuf;
1246 }
1247
1248 if (method->methodName) {
1249 printf("%2d %s%c %8lld%c%s%s.%s %s\n", threadId,
1250 actionStr[action], mismatch ? '!' : ' ',
1251 elapsedTime, depthNote,
1252 spaces + (MAX_STACK_DEPTH - printDepth),
1253 method->className, method->methodName, method->signature);
1254 } else {
1255 printf("%2d %s%c %8lld%c%s%s\n", threadId,
1256 actionStr[action], mismatch ? '!' : ' ',
1257 elapsedTime, depthNote,
1258 spaces + (MAX_STACK_DEPTH - printDepth),
1259 method->className);
1260 }
1261
1262 if (action != METHOD_TRACE_ENTER) {
1263 traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */
1264 lastEnter = 0;
1265 }
1266
1267 mismatch = 0;
1268 }
1269
1270 bail:
1271 if (dataFp != NULL)
1272 fclose(dataFp);
1273 if (pKeys != NULL)
1274 freeDataKeys(pKeys);
1275 }
1276
1277 /* This routine adds the given time to the parent and child methods.
1278 * This is called when the child routine exits, after the child has
1279 * been popped from the stack. The elapsedTime parameter is the
1280 * duration of the child routine, including time spent in called routines.
1281 */
1282 void addInclusiveTime(MethodEntry *parent, MethodEntry *child,
1283 uint64_t elapsedTime)
1284 {
1285 TimedMethod *pTimed;
1286
1287 #if 0
1288 bool verbose = false;
1289 if (strcmp(child->className, debugClassName) == 0)
1290 verbose = true;
1291 #endif
1292
1293 int childIsRecursive = (child->recursiveEntries > 0);
1294 int parentIsRecursive = (parent->recursiveEntries > 1);
1295
1296 if (child->recursiveEntries == 0) {
1297 child->elapsedInclusive += elapsedTime;
1298 } else if (child->recursiveEntries == 1) {
1299 child->recursiveInclusive += elapsedTime;
1300 }
1301 child->numCalls[childIsRecursive] += 1;
1302
1303 #if 0
1304 if (verbose) {
1305 fprintf(stderr,
1306 "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n",
1307 child->className, child->recursiveEntries,
1308 elapsedTime, child->elapsedInclusive,
1309 child->recursiveInclusive);
1310 }
1311 #endif
1312
1313 /* Find the child method in the parent */
1314 TimedMethod *children = parent->children[parentIsRecursive];
1315 for (pTimed = children; pTimed; pTimed = pTimed->next) {
1316 if (pTimed->method == child) {
1317 pTimed->elapsedInclusive += elapsedTime;
1318 pTimed->numCalls += 1;
1319 break;
1320 }
1321 }
1322 if (pTimed == NULL) {
1323 /* Allocate a new TimedMethod */
1324 pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
1325 pTimed->elapsedInclusive = elapsedTime;
1326 pTimed->numCalls = 1;
1327 pTimed->method = child;
1328
1329 /* Add it to the front of the list */
1330 pTimed->next = children;
1331 parent->children[parentIsRecursive] = pTimed;
1332 }
1333
1334 /* Find the parent method in the child */
1335 TimedMethod *parents = child->parents[childIsRecursive];
1336 for (pTimed = parents; pTimed; pTimed = pTimed->next) {
1337 if (pTimed->method == parent) {
1338 pTimed->elapsedInclusive += elapsedTime;
1339 pTimed->numCalls += 1;
1340 break;
1341 }
1342 }
1343 if (pTimed == NULL) {
1344 /* Allocate a new TimedMethod */
1345 pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
1346 pTimed->elapsedInclusive = elapsedTime;
1347 pTimed->numCalls = 1;
1348 pTimed->method = parent;
1349
1350 /* Add it to the front of the list */
1351 pTimed->next = parents;
1352 child->parents[childIsRecursive] = pTimed;
1353 }
1354
1355 #if 0
1356 if (verbose) {
1357 fprintf(stderr,
1358 " %s %d eI: %lld\n",
1359 parent->className, parent->recursiveEntries,
1360 pTimed->elapsedInclusive);
1361 }
1362 #endif
1363 }
1364
1365 /* Sorts a linked list and returns a newly allocated array containing
1366 * the sorted entries.
1367 */
1368 TimedMethod *sortTimedMethodList(TimedMethod *list, int *num)
1369 {
1370 int ii;
1371 TimedMethod *pTimed, *sorted;
1372
1373 /* Count the elements */
1374 int num_entries = 0;
1375 for (pTimed = list; pTimed; pTimed = pTimed->next)
1376 num_entries += 1;
1377 *num = num_entries;
1378 if (num_entries == 0)
1379 return NULL;
1380
1381 /* Copy all the list elements to a new array and sort them */
1382 sorted = (TimedMethod *) malloc(sizeof(TimedMethod) * num_entries);
1383 for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii)
1384 memcpy(&sorted[ii], pTimed, sizeof(TimedMethod));
1385 qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod);
1386
1387 /* Fix up the "next" pointers so that they work. */
1388 for (ii = 0; ii < num_entries - 1; ++ii)
1389 sorted[ii].next = &sorted[ii + 1];
1390 sorted[num_entries - 1].next = NULL;
1391
1392 return sorted;
1393 }
1394
1395 /* Define flag values for printInclusiveMethod() */
1396 static const int kIsRecursive = 1;
1397
1398 /* This prints the inclusive stats for all the parents or children of a
1399 * method, depending on the list that is passed in.
1400 */
1401 void printInclusiveMethod(MethodEntry *method, TimedMethod *list, int numCalls,
1402 int flags)
1403 {
1404 int num;
1405 TimedMethod *pTimed;
1406 char buf[80];
1407 char *anchor_close;
1408 char *spaces = " "; /* 6 spaces */
1409 int num_spaces = strlen(spaces);
1410 char *space_ptr = &spaces[num_spaces];
1411 char *className, *methodName, *signature;
1412 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1413 char signatureBuf[HTML_BUFSIZE];
1414
1415 anchor_close = "";
1416 if (gOptions.outputHtml)
1417 anchor_close = "</a>";
1418
1419 TimedMethod *sorted = sortTimedMethodList(list, &num);
1420 double methodTotal = method->elapsedInclusive;
1421 for (pTimed = sorted; pTimed; pTimed = pTimed->next) {
1422 MethodEntry *relative = pTimed->method;
1423 className = (char*)(relative->className);
1424 methodName = (char*)(relative->methodName);
1425 signature = (char*)(relative->signature);
1426 double per = 100.0 * pTimed->elapsedInclusive / methodTotal;
1427 sprintf(buf, "[%d]", relative->index);
1428 if (gOptions.outputHtml) {
1429 int len = strlen(buf);
1430 if (len > num_spaces)
1431 len = num_spaces;
1432 sprintf(buf, "<a href=\"#m%d\">[%d]",
1433 relative->index, relative->index);
1434 space_ptr = &spaces[len];
1435 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1436 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
1437 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
1438 }
1439 int nCalls = numCalls;
1440 if (nCalls == 0)
1441 nCalls = relative->numCalls[0] + relative->numCalls[1];
1442 if (relative->methodName) {
1443 if (flags & kIsRecursive) {
1444 // Don't display percentages for recursive functions
1445 printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
1446 "", "", "",
1447 space_ptr, buf, anchor_close,
1448 pTimed->numCalls, nCalls,
1449 pTimed->elapsedInclusive,
1450 className, methodName, signature);
1451 } else {
1452 printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
1453 "", "", per,
1454 space_ptr, buf, anchor_close,
1455 pTimed->numCalls, nCalls,
1456 pTimed->elapsedInclusive,
1457 className, methodName, signature);
1458 }
1459 } else {
1460 if (flags & kIsRecursive) {
1461 // Don't display percentages for recursive functions
1462 printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s\n",
1463 "", "", "",
1464 space_ptr, buf, anchor_close,
1465 pTimed->numCalls, nCalls,
1466 pTimed->elapsedInclusive,
1467 className);
1468 } else {
1469 printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s\n",
1470 "", "", per,
1471 space_ptr, buf, anchor_close,
1472 pTimed->numCalls, nCalls,
1473 pTimed->elapsedInclusive,
1474 className);
1475 }
1476 }
1477 }
1478 }
1479
1480 void countRecursiveEntries(CallStack *pStack, int top, MethodEntry *method)
1481 {
1482 int ii;
1483
1484 method->recursiveEntries = 0;
1485 for (ii = 0; ii < top; ++ii) {
1486 if (pStack->calls[ii].method == method)
1487 method->recursiveEntries += 1;
1488 }
1489 }
1490
1491 void stackDump(CallStack *pStack, int top)
1492 {
1493 int ii;
1494
1495 for (ii = 0; ii < top; ++ii) {
1496 MethodEntry *method = pStack->calls[ii].method;
1497 uint64_t entryTime = pStack->calls[ii].entryTime;
1498 if (method->methodName) {
1499 fprintf(stderr, " %2d: %8llu %s.%s %s\n", ii, entryTime,
1500 method->className, method->methodName, method->signature);
1501 } else {
1502 fprintf(stderr, " %2d: %8llu %s\n", ii, entryTime, method->className);
1503 }
1504 }
1505 }
1506
1507 void outputTableOfContents()
1508 {
1509 printf("<a name=\"contents\"></a>\n");
1510 printf("<h2>Table of Contents</h2>\n");
1511 printf("<ul>\n");
1512 printf(" <li><a href=\"#exclusive\">Exclusive profile</a></li>\n");
1513 printf(" <li><a href=\"#inclusive\">Inclusive profile</a></li>\n");
1514 printf(" <li><a href=\"#class\">Class/method profile</a></li>\n");
1515 printf(" <li><a href=\"#method\">Method/class profile</a></li>\n");
1516 printf("</ul>\n\n");
1517 }
1518
1519 void outputNavigationBar()
1520 {
1521 printf("<a href=\"#contents\">[Top]</a>\n");
1522 printf("<a href=\"#exclusive\">[Exclusive]</a>\n");
1523 printf("<a href=\"#inclusive\">[Inclusive]</a>\n");
1524 printf("<a href=\"#class\">[Class]</a>\n");
1525 printf("<a href=\"#method\">[Method]</a>\n");
1526 printf("<br><br>\n");
1527 }
1528
1529 void printExclusiveProfile(MethodEntry **pMethods, int numMethods,
1530 uint64_t sumThreadTime)
1531 {
1532 int ii;
1533 MethodEntry* method;
1534 double total, sum, per, sum_per;
1535 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1536 char signatureBuf[HTML_BUFSIZE];
1537 char anchor_buf[80];
1538 char *anchor_close = "";
1539
1540 total = sumThreadTime;
1541 anchor_buf[0] = 0;
1542 if (gOptions.outputHtml) {
1543 anchor_close = "</a>";
1544 printf("<a name=\"exclusive\"></a>\n");
1545 printf("<hr>\n");
1546 outputNavigationBar();
1547 } else {
1548 printf("\n%s\n", profileSeparator);
1549 }
1550
1551 /* First, sort the methods into decreasing order of inclusive
1552 * elapsed time so that we can assign the method indices.
1553 */
1554 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
1555
1556 for (ii = 0; ii < numMethods; ++ii)
1557 pMethods[ii]->index = ii;
1558
1559 /* Sort the methods into decreasing order of exclusive elapsed time.
1560 */
1561 qsort(pMethods, numMethods, sizeof(MethodEntry*),
1562 compareElapsedExclusive);
1563
1564 printf("Total cycles: %llu\n\n", sumThreadTime);
1565 if (gOptions.outputHtml) {
1566 printf("<br><br>\n");
1567 }
1568 printf("Exclusive elapsed times for each method, not including time spent in\n");
1569 printf("children, sorted by exclusive time.\n\n");
1570 if (gOptions.outputHtml) {
1571 printf("<br><br>\n<pre>\n");
1572 }
1573
1574 printf(" Usecs self %% sum %% Method\n");
1575 sum = 0;
1576
1577 for (ii = 0; ii < numMethods; ++ii) {
1578 char *className, *methodName, *signature;
1579
1580 method = pMethods[ii];
1581 /* Don't show methods with zero cycles */
1582 if (method->elapsedExclusive == 0)
1583 break;
1584 className = (char*)(method->className);
1585 methodName = (char*)(method->methodName);
1586 signature = (char*)(method->signature);
1587 sum += method->elapsedExclusive;
1588 per = 100.0 * method->elapsedExclusive / total;
1589 sum_per = 100.0 * sum / total;
1590 if (gOptions.outputHtml) {
1591 sprintf(anchor_buf, "<a href=\"#m%d\">", method->index);
1592 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1593 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
1594 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
1595 }
1596 if (method->methodName) {
1597 printf("%9llu %6.2f %6.2f %s[%d]%s %s.%s %s\n",
1598 method->elapsedExclusive, per, sum_per,
1599 anchor_buf, method->index, anchor_close,
1600 className, methodName, signature);
1601 } else {
1602 printf("%9llu %6.2f %6.2f %s[%d]%s %s\n",
1603 method->elapsedExclusive, per, sum_per,
1604 anchor_buf, method->index, anchor_close,
1605 className);
1606 }
1607 }
1608 if (gOptions.outputHtml) {
1609 printf("</pre>\n");
1610 }
1611 }
1612
1613 /* check to make sure that the child method meets the threshold of the parent */
1614 int checkThreshold(MethodEntry* parent, MethodEntry* child)
1615 {
1616 double parentTime = parent->elapsedInclusive;
1617 double childTime = child->elapsedInclusive;
1618 int64_t percentage = (childTime / parentTime) * 100.0;
1619 return (percentage < gOptions.threshold) ? 0 : 1;
1620 }
1621
1622 void createLabels(FILE* file, MethodEntry* method)
1623 {
1624 fprintf(file, "node%d[label = \"[%d] %s.%s (%llu, %llu, %d)\"]\n",
1625 method->index, method->index, method->className, method->methodName,
1626 method->elapsedInclusive / 1000,
1627 method->elapsedExclusive / 1000,
1628 method->numCalls[0]);
1629
1630 method->graphState = GRAPH_LABEL_VISITED;
1631
1632 TimedMethod* child;
1633 for (child = method->children[0] ; child ; child = child->next) {
1634 MethodEntry* childMethod = child->method;
1635
1636 if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && checkThreshold(method, childMethod)) {
1637 createLabels(file, child->method);
1638 }
1639 }
1640 }
1641
1642 void createLinks(FILE* file, MethodEntry* method)
1643 {
1644 method->graphState |= GRAPH_NODE_VISITED;
1645
1646 TimedMethod* child;
1647 for (child = method->children[0] ; child ; child = child->next) {
1648 MethodEntry* childMethod = child->method;
1649 if (checkThreshold(method, child->method)) {
1650 fprintf(file, "node%d -> node%d\n", method->index, child->method->index);
1651 // only visit children that haven't been visited before
1652 if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) {
1653 createLinks(file, child->method);
1654 }
1655 }
1656 }
1657 }
1658
1659 void createInclusiveProfileGraphNew(DataKeys* dataKeys)
1660 {
1661 // create a temporary file in /tmp
1662 char path[FILENAME_MAX];
1663 if (gOptions.keepDotFile) {
1664 snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName);
1665 } else {
1666 snprintf(path, FILENAME_MAX, "dot-%d-%d.dot", (int)time(NULL), rand());
1667 }
1668
1669 FILE* file = fopen(path, "w+");
1670
1671 fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n");
1672
1673 createLabels(file, dataKeys->methods);
1674 createLinks(file, dataKeys->methods);
1675
1676 fprintf(file, "}");
1677 fclose(file);
1678
1679 // now that we have the dot file generate the image
1680 char command[1024];
1681 snprintf(command, 1024, "dot -Tpng -o \"%s\" \"%s\"", gOptions.graphFileName, path);
1682
1683 system(command);
1684
1685 if (! gOptions.keepDotFile) {
1686 remove(path);
1687 }
1688 }
1689
1690 void printInclusiveProfile(MethodEntry **pMethods, int numMethods,
1691 uint64_t sumThreadTime)
1692 {
1693 int ii;
1694 MethodEntry* method;
1695 double total, sum, per, sum_per;
1696 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1697 char signatureBuf[HTML_BUFSIZE];
1698 char anchor_buf[80];
1699 char *anchor_close = "";
1700
1701 total = sumThreadTime;
1702 anchor_buf[0] = 0;
1703 if (gOptions.outputHtml) {
1704 anchor_close = "</a>";
1705 printf("<a name=\"inclusive\"></a>\n");
1706 printf("<hr>\n");
1707 outputNavigationBar();
1708 } else {
1709 printf("\n%s\n", profileSeparator);
1710 }
1711
1712 /* Sort the methods into decreasing order of inclusive elapsed time. */
1713 qsort(pMethods, numMethods, sizeof(MethodEntry*),
1714 compareElapsedInclusive);
1715
1716 printf("\nInclusive elapsed times for each method and its parents and children,\n");
1717 printf("sorted by inclusive time.\n\n");
1718
1719 if (gOptions.outputHtml) {
1720 printf("<br><br>\n<pre>\n");
1721 }
1722
1723 printf("index %%/total %%/self index calls usecs name\n");
1724 for (ii = 0; ii < numMethods; ++ii) {
1725 int num;
1726 TimedMethod *pTimed;
1727 double excl_per;
1728 char buf[40];
1729 char *className, *methodName, *signature;
1730
1731 method = pMethods[ii];
1732 /* Don't show methods with zero cycles */
1733 if (method->elapsedInclusive == 0)
1734 break;
1735
1736 className = (char*)(method->className);
1737 methodName = (char*)(method->methodName);
1738 signature = (char*)(method->signature);
1739
1740 if (gOptions.outputHtml) {
1741 printf("<a name=\"m%d\"></a>", method->index);
1742 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1743 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
1744 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
1745 }
1746 printf("----------------------------------------------------\n");
1747
1748 /* Sort and print the parents */
1749 int numCalls = method->numCalls[0] + method->numCalls[1];
1750 printInclusiveMethod(method, method->parents[0], numCalls, 0);
1751 if (method->parents[1]) {
1752 printf(" +++++++++++++++++++++++++\n");
1753 printInclusiveMethod(method, method->parents[1], numCalls,
1754 kIsRecursive);
1755 }
1756
1757 per = 100.0 * method->elapsedInclusive / total;
1758 sprintf(buf, "[%d]", ii);
1759 if (method->methodName) {
1760 printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s.%s %s\n",
1761 buf,
1762 per, "", "", method->numCalls[0], method->numCalls[1],
1763 method->elapsedInclusive,
1764 className, methodName, signature);
1765 } else {
1766 printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s\n",
1767 buf,
1768 per, "", "", method->numCalls[0], method->numCalls[1],
1769 method->elapsedInclusive,
1770 className);
1771 }
1772 excl_per = 100.0 * method->topExclusive / method->elapsedInclusive;
1773 printf("%6s %5s %5.1f%% %6s %6s %6s %9llu\n",
1774 "", "", excl_per, "excl", "", "", method->topExclusive);
1775
1776 /* Sort and print the children */
1777 printInclusiveMethod(method, method->children[0], 0, 0);
1778 if (method->children[1]) {
1779 printf(" +++++++++++++++++++++++++\n");
1780 printInclusiveMethod(method, method->children[1], 0,
1781 kIsRecursive);
1782 }
1783 }
1784 if (gOptions.outputHtml) {
1785 printf("</pre>\n");
1786 }
1787 }
1788
1789 void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
1790 {
1791 int ii;
1792
1793 /* Sort the methods into alphabetical order to find the unique class
1794 * names.
1795 */
1796 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames);
1797
1798 /* Count the number of unique class names. */
1799 const char *currentClassName = "";
1800 const char *firstClassName = NULL;
1801 traceData->numClasses = 0;
1802 for (ii = 0; ii < numMethods; ++ii) {
1803 if (pMethods[ii]->methodName == NULL) {
1804 continue;
1805 }
1806 if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
1807 // Remember the first one
1808 if (firstClassName == NULL) {
1809 firstClassName = pMethods[ii]->className;
1810 }
1811 traceData->numClasses += 1;
1812 currentClassName = pMethods[ii]->className;
1813 }
1814 }
1815
1816 if (traceData->numClasses == 0) {
1817 traceData->classes = NULL;
1818 return;
1819 }
1820
1821 /* Allocate space for all of the unique class names */
1822 traceData->classes = (ClassEntry *) malloc(sizeof(ClassEntry) * traceData->numClasses);
1823
1824 /* Initialize the classes array */
1825 memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses);
1826 ClassEntry *pClass = traceData->classes;
1827 pClass->className = currentClassName = firstClassName;
1828 int prevNumMethods = 0;
1829 for (ii = 0; ii < numMethods; ++ii) {
1830 if (pMethods[ii]->methodName == NULL) {
1831 continue;
1832 }
1833 if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
1834 pClass->numMethods = prevNumMethods;
1835 (++pClass)->className = currentClassName = pMethods[ii]->className;
1836 prevNumMethods = 0;
1837 }
1838 prevNumMethods += 1;
1839 }
1840 pClass->numMethods = prevNumMethods;
1841
1842 /* Create the array of MethodEntry pointers for each class */
1843 pClass = NULL;
1844 currentClassName = "";
1845 int nextMethod = 0;
1846 for (ii = 0; ii < numMethods; ++ii) {
1847 if (pMethods[ii]->methodName == NULL) {
1848 continue;
1849 }
1850 if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
1851 currentClassName = pMethods[ii]->className;
1852 if (pClass == NULL)
1853 pClass = traceData->classes;
1854 else
1855 pClass++;
1856 /* Allocate space for the methods array */
1857 int nbytes = sizeof(MethodEntry*) * pClass->numMethods;
1858 pClass->methods = (MethodEntry**) malloc(nbytes);
1859 nextMethod = 0;
1860 }
1861 pClass->methods[nextMethod++] = pMethods[ii];
1862 }
1863 }
1864
1865 /* Prints a number of html non-breaking spaces according so that the length
1866 * of the string "buf" is at least "width" characters wide. If width is
1867 * negative, then trailing spaces are added instead of leading spaces.
1868 */
1869 void printHtmlField(char *buf, int width)
1870 {
1871 int ii;
1872
1873 int leadingSpaces = 1;
1874 if (width < 0) {
1875 width = -width;
1876 leadingSpaces = 0;
1877 }
1878 int len = strlen(buf);
1879 int numSpaces = width - len;
1880 if (numSpaces <= 0) {
1881 printf("%s", buf);
1882 return;
1883 }
1884 if (leadingSpaces == 0)
1885 printf("%s", buf);
1886 for (ii = 0; ii < numSpaces; ++ii)
1887 printf("&nbsp;");
1888 if (leadingSpaces == 1)
1889 printf("%s", buf);
1890 }
1891
1892 void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime)
1893 {
1894 int ii, jj;
1895 MethodEntry* method;
1896 double total, sum, per, sum_per;
1897 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
1898 char signatureBuf[HTML_BUFSIZE];
1899
1900 total = sumThreadTime;
1901 if (gOptions.outputHtml) {
1902 printf("<a name=\"class\"></a>\n");
1903 printf("<hr>\n");
1904 outputNavigationBar();
1905 } else {
1906 printf("\n%s\n", profileSeparator);
1907 }
1908
1909 if (traceData->numClasses == 0) {
1910 printf("\nNo classes.\n");
1911 if (gOptions.outputHtml) {
1912 printf("<br><br>\n");
1913 }
1914 return;
1915 }
1916
1917 printf("\nExclusive elapsed time for each class, summed over all the methods\n");
1918 printf("in the class.\n\n");
1919 if (gOptions.outputHtml) {
1920 printf("<br><br>\n");
1921 }
1922
1923 /* For each class, sum the exclusive times in all of the methods
1924 * in that class. Also sum the number of method calls. Also
1925 * sort the methods so the most expensive appear at the top.
1926 */
1927 ClassEntry *pClass = traceData->classes;
1928 for (ii = 0; ii < traceData->numClasses; ++ii, ++pClass) {
1929 //printf("%s %d methods\n", pClass->className, pClass->numMethods);
1930 int numMethods = pClass->numMethods;
1931 for (jj = 0; jj < numMethods; ++jj) {
1932 method = pClass->methods[jj];
1933 pClass->elapsedExclusive += method->elapsedExclusive;
1934 pClass->numCalls[0] += method->numCalls[0];
1935 pClass->numCalls[1] += method->numCalls[1];
1936 }
1937
1938 /* Sort the methods into decreasing order of exclusive time */
1939 qsort(pClass->methods, numMethods, sizeof(MethodEntry*),
1940 compareElapsedExclusive);
1941 }
1942
1943 /* Allocate an array of pointers to the classes for more efficient
1944 * sorting.
1945 */
1946 ClassEntry **pClasses;
1947 pClasses = (ClassEntry**) malloc(sizeof(ClassEntry*) * traceData->numClasses);
1948 for (ii = 0; ii < traceData->numClasses; ++ii)
1949 pClasses[ii] = &traceData->classes[ii];
1950
1951 /* Sort the classes into decreasing order of exclusive time */
1952 qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive);
1953
1954 if (gOptions.outputHtml) {
1955 printf("<div class=\"header\"><span class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
1956 printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Class</div>\n");
1957 } else {
1958 printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n");
1959 }
1960
1961 sum = 0;
1962 for (ii = 0; ii < traceData->numClasses; ++ii) {
1963 char *className, *methodName, *signature;
1964
1965 /* Skip classes with zero cycles */
1966 pClass = pClasses[ii];
1967 if (pClass->elapsedExclusive == 0)
1968 break;
1969
1970 per = 100.0 * pClass->elapsedExclusive / total;
1971 sum += pClass->elapsedExclusive;
1972 sum_per = 100.0 * sum / total;
1973 className = (char*)(pClass->className);
1974 if (gOptions.outputHtml) {
1975 char buf[80];
1976
1977 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
1978 printf("<div class=\"link\" onClick=\"javascript:toggle('d%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xd%d\">+</span>", ii, ii);
1979 sprintf(buf, "%llu", pClass->elapsedExclusive);
1980 printHtmlField(buf, 9);
1981 printf(" ");
1982 sprintf(buf, "%.1f", per);
1983 printHtmlField(buf, 7);
1984 printf(" ");
1985 sprintf(buf, "%.1f", sum_per);
1986 printHtmlField(buf, 7);
1987 printf(" ");
1988 sprintf(buf, "%d", pClass->numCalls[0]);
1989 printHtmlField(buf, 6);
1990 printf("+");
1991 sprintf(buf, "%d", pClass->numCalls[1]);
1992 printHtmlField(buf, -6);
1993 printf(" ");
1994 printf("%s", className);
1995 printf("</div>\n");
1996 printf("<div class=\"parent\" id=\"d%d\">\n", ii);
1997 } else {
1998 printf("---------------------------------------------\n");
1999 printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
2000 pClass->elapsedExclusive, per, sum_per,
2001 pClass->numCalls[0], pClass->numCalls[1],
2002 className);
2003 }
2004
2005 int numMethods = pClass->numMethods;
2006 double classExclusive = pClass->elapsedExclusive;
2007 double sumMethods = 0;
2008 for (jj = 0; jj < numMethods; ++jj) {
2009 method = pClass->methods[jj];
2010 methodName = (char*)(method->methodName);
2011 signature = (char*)(method->signature);
2012 per = 100.0 * method->elapsedExclusive / classExclusive;
2013 sumMethods += method->elapsedExclusive;
2014 sum_per = 100.0 * sumMethods / classExclusive;
2015 if (gOptions.outputHtml) {
2016 char buf[80];
2017
2018 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
2019 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
2020 printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
2021 sprintf(buf, "%llu", method->elapsedExclusive);
2022 printHtmlField(buf, 9);
2023 printf("&nbsp;");
2024 sprintf(buf, "%llu", method->elapsedInclusive);
2025 printHtmlField(buf, 9);
2026 printf("&nbsp;");
2027 sprintf(buf, "%.1f", per);
2028 printHtmlField(buf, 7);
2029 printf("&nbsp;");
2030 sprintf(buf, "%.1f", sum_per);
2031 printHtmlField(buf, 7);
2032 printf("&nbsp;");
2033 sprintf(buf, "%d", method->numCalls[0]);
2034 printHtmlField(buf, 6);
2035 printf("+");
2036 sprintf(buf, "%d", method->numCalls[1]);
2037 printHtmlField(buf, -6);
2038 printf("&nbsp;");
2039 printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s&nbsp;%s",
2040 method->index, method->index, methodName, signature);
2041 printf("</div>\n");
2042 } else {
2043 printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s %s\n",
2044 method->elapsedExclusive,
2045 method->elapsedInclusive,
2046 per, sum_per,
2047 method->numCalls[0], method->numCalls[1],
2048 method->index, methodName, signature);
2049 }
2050 }
2051 if (gOptions.outputHtml) {
2052 printf("</div>\n");
2053 }
2054 }
2055 }
2056
2057 void createUniqueMethodList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
2058 {
2059 int ii;
2060
2061 /* Sort the methods into alphabetical order of method names
2062 * to find the unique method names.
2063 */
2064 qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames);
2065
2066 /* Count the number of unique method names, ignoring class and
2067 * signature.
2068 */
2069 const char *currentMethodName = "";
2070 traceData->numUniqueMethods = 0;
2071 for (ii = 0; ii < numMethods; ++ii) {
2072 if (pMethods[ii]->methodName == NULL)
2073 continue;
2074 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
2075 traceData->numUniqueMethods += 1;
2076 currentMethodName = pMethods[ii]->methodName;
2077 }
2078 }
2079 if (traceData->numUniqueMethods == 0)
2080 return;
2081
2082 /* Allocate space for pointers to all of the unique methods */
2083 int nbytes = sizeof(UniqueMethodEntry) * traceData->numUniqueMethods;
2084 traceData->uniqueMethods = (UniqueMethodEntry *) malloc(nbytes);
2085
2086 /* Initialize the uniqueMethods array */
2087 memset(traceData->uniqueMethods, 0, nbytes);
2088 UniqueMethodEntry *pUnique = traceData->uniqueMethods;
2089 currentMethodName = NULL;
2090 int prevNumMethods = 0;
2091 for (ii = 0; ii < numMethods; ++ii) {
2092 if (pMethods[ii]->methodName == NULL)
2093 continue;
2094 if (currentMethodName == NULL)
2095 currentMethodName = pMethods[ii]->methodName;
2096 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
2097 currentMethodName = pMethods[ii]->methodName;
2098 pUnique->numMethods = prevNumMethods;
2099 pUnique++;
2100 prevNumMethods = 0;
2101 }
2102 prevNumMethods += 1;
2103 }
2104 pUnique->numMethods = prevNumMethods;
2105
2106 /* Create the array of MethodEntry pointers for each unique method */
2107 pUnique = NULL;
2108 currentMethodName = "";
2109 int nextMethod = 0;
2110 for (ii = 0; ii < numMethods; ++ii) {
2111 if (pMethods[ii]->methodName == NULL)
2112 continue;
2113 if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
2114 currentMethodName = pMethods[ii]->methodName;
2115 if (pUnique == NULL)
2116 pUnique = traceData->uniqueMethods;
2117 else
2118 pUnique++;
2119 /* Allocate space for the methods array */
2120 int nbytes = sizeof(MethodEntry*) * pUnique->numMethods;
2121 pUnique->methods = (MethodEntry**) malloc(nbytes);
2122 nextMethod = 0;
2123 }
2124 pUnique->methods[nextMethod++] = pMethods[ii];
2125 }
2126 }
2127
2128 void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime)
2129 {
2130 int ii, jj;
2131 MethodEntry* method;
2132 double total, sum, per, sum_per;
2133 char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
2134 char signatureBuf[HTML_BUFSIZE];
2135
2136 if (traceData->numUniqueMethods == 0)
2137 return;
2138
2139 total = sumThreadTime;
2140 if (gOptions.outputHtml) {
2141 printf("<a name=\"method\"></a>\n");
2142 printf("<hr>\n");
2143 outputNavigationBar();
2144 } else {
2145 printf("\n%s\n", profileSeparator);
2146 }
2147
2148 printf("\nExclusive elapsed time for each method, summed over all the classes\n");
2149 printf("that contain a method with the same name.\n\n");
2150 if (gOptions.outputHtml) {
2151 printf("<br><br>\n");
2152 }
2153
2154 /* For each unique method, sum the exclusive times in all of the methods
2155 * with the same name. Also sum the number of method calls. Also
2156 * sort the methods so the most expensive appear at the top.
2157 */
2158 UniqueMethodEntry *pUnique = traceData->uniqueMethods;
2159 for (ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) {
2160 int numMethods = pUnique->numMethods;
2161 for (jj = 0; jj < numMethods; ++jj) {
2162 method = pUnique->methods[jj];
2163 pUnique->elapsedExclusive += method->elapsedExclusive;
2164 pUnique->numCalls[0] += method->numCalls[0];
2165 pUnique->numCalls[1] += method->numCalls[1];
2166 }
2167
2168 /* Sort the methods into decreasing order of exclusive time */
2169 qsort(pUnique->methods, numMethods, sizeof(MethodEntry*),
2170 compareElapsedExclusive);
2171 }
2172
2173 /* Allocate an array of pointers to the methods for more efficient
2174 * sorting.
2175 */
2176 UniqueMethodEntry **pUniqueMethods;
2177 int nbytes = sizeof(UniqueMethodEntry*) * traceData->numUniqueMethods;
2178 pUniqueMethods = (UniqueMethodEntry**) malloc(nbytes);
2179 for (ii = 0; ii < traceData->numUniqueMethods; ++ii)
2180 pUniqueMethods[ii] = &traceData->uniqueMethods[ii];
2181
2182 /* Sort the methods into decreasing order of exclusive time */
2183 qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*),
2184 compareUniqueExclusive);
2185
2186 if (gOptions.outputHtml) {
2187 printf("<div class=\"header\"><span class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
2188 printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Method</div>\n");
2189 } else {
2190 printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n");
2191 }
2192
2193 sum = 0;
2194 for (ii = 0; ii < traceData->numUniqueMethods; ++ii) {
2195 char *className, *methodName, *signature;
2196
2197 /* Skip methods with zero cycles */
2198 pUnique = pUniqueMethods[ii];
2199 if (pUnique->elapsedExclusive == 0)
2200 break;
2201
2202 per = 100.0 * pUnique->elapsedExclusive / total;
2203 sum += pUnique->elapsedExclusive;
2204 sum_per = 100.0 * sum / total;
2205 methodName = (char*)(pUnique->methods[0]->methodName);
2206 if (gOptions.outputHtml) {
2207 char buf[80];
2208
2209 methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
2210 printf("<div class=\"link\" onClick=\"javascript:toggle('e%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xe%d\">+</span>", ii, ii);
2211 sprintf(buf, "%llu", pUnique->elapsedExclusive);
2212 printHtmlField(buf, 9);
2213 printf(" ");
2214 sprintf(buf, "%.1f", per);
2215 printHtmlField(buf, 7);
2216 printf(" ");
2217 sprintf(buf, "%.1f", sum_per);
2218 printHtmlField(buf, 7);
2219 printf(" ");
2220 sprintf(buf, "%d", pUnique->numCalls[0]);
2221 printHtmlField(buf, 6);
2222 printf("+");
2223 sprintf(buf, "%d", pUnique->numCalls[1]);
2224 printHtmlField(buf, -6);
2225 printf(" ");
2226 printf("%s", methodName);
2227 printf("</div>\n");
2228 printf("<div class=\"parent\" id=\"e%d\">\n", ii);
2229 } else {
2230 printf("---------------------------------------------\n");
2231 printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
2232 pUnique->elapsedExclusive, per, sum_per,
2233 pUnique->numCalls[0], pUnique->numCalls[1],
2234 methodName);
2235 }
2236 int numMethods = pUnique->numMethods;
2237 double methodExclusive = pUnique->elapsedExclusive;
2238 double sumMethods = 0;
2239 for (jj = 0; jj < numMethods; ++jj) {
2240 method = pUnique->methods[jj];
2241 className = (char*)(method->className);
2242 signature = (char*)(method->signature);
2243 per = 100.0 * method->elapsedExclusive / methodExclusive;
2244 sumMethods += method->elapsedExclusive;
2245 sum_per = 100.0 * sumMethods / methodExclusive;
2246 if (gOptions.outputHtml) {
2247 char buf[80];
2248
2249 className = htmlEscape(className, classBuf, HTML_BUFSIZE);
2250 signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
2251 printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
2252 sprintf(buf, "%llu", method->elapsedExclusive);
2253 printHtmlField(buf, 9);
2254 printf("&nbsp;");
2255 sprintf(buf, "%llu", method->elapsedInclusive);
2256 printHtmlField(buf, 9);
2257 printf("&nbsp;");
2258 sprintf(buf, "%.1f", per);
2259 printHtmlField(buf, 7);
2260 printf("&nbsp;");
2261 sprintf(buf, "%.1f", sum_per);
2262 printHtmlField(buf, 7);
2263 printf("&nbsp;");
2264 sprintf(buf, "%d", method->numCalls[0]);
2265 printHtmlField(buf, 6);
2266 printf("+");
2267 sprintf(buf, "%d", method->numCalls[1]);
2268 printHtmlField(buf, -6);
2269 printf("&nbsp;");
2270 printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s.%s&nbsp;%s",
2271 method->index, method->index,
2272 className, methodName, signature);
2273 printf("</div>\n");
2274 } else {
2275 printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n",
2276 method->elapsedExclusive,
2277 method->elapsedInclusive,
2278 per, sum_per,
2279 method->numCalls[0], method->numCalls[1],
2280 method->index, className, methodName, signature);
2281 }
2282 }
2283 if (gOptions.outputHtml) {
2284 printf("</div>\n");
2285 }
2286 }
2287 }
2288
2289 /*
2290 * Read the key and data files and return the MethodEntries for those files
2291 */
2292 DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime)
2293 {
2294 DataKeys* dataKeys = NULL;
2295 MethodEntry **pMethods = NULL;
2296 MethodEntry* method;
2297 FILE* dataFp = NULL;
2298 DataHeader dataHeader;
2299 int ii;
2300 uint64_t currentTime;
2301 MethodEntry* caller;
2302
2303 dataFp = fopen(traceFileName, "rb");
2304 if (dataFp == NULL)
2305 goto bail;
2306
2307 if ((dataKeys = parseKeys(dataFp, 0)) == NULL)
2308 goto bail;
2309
2310 if (parseDataHeader(dataFp, &dataHeader) < 0)
2311 goto bail;
2312
2313 #if 0
2314 FILE *dumpStream = fopen("debug", "w");
2315 #endif
2316 while (1) {
2317 int threadId;
2318 unsigned int methodVal;
2319 int action;
2320 int64_t methodId;
2321 CallStack *pStack;
2322 /*
2323 * Extract values from file.
2324 */
2325 if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &currentTime))
2326 break;
2327
2328 action = METHOD_ACTION(methodVal);
2329 methodId = METHOD_ID(methodVal);
2330
2331 /* Get the call stack for this thread */
2332 pStack = traceData->stacks[threadId];
2333
2334 /* If there is no call stack yet for this thread, then allocate one */
2335 if (pStack == NULL) {
2336 pStack = malloc(sizeof(CallStack));
2337 pStack->top = 0;
2338 pStack->lastEventTime = currentTime;
2339 pStack->threadStartTime = currentTime;
2340 traceData->stacks[threadId] = pStack;
2341 }
2342
2343 /* Lookup the current method */
2344 method = lookupMethod(dataKeys, methodId);
2345 if (method == NULL)
2346 method = &dataKeys->methods[UNKNOWN_INDEX];
2347
2348 #if 0
2349 if (method->methodName) {
2350 fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
2351 threadId, currentTime, action, pStack->threadStartTime,
2352 method->recursiveEntries,
2353 pStack->top, method->className, method->methodName,
2354 method->signature);
2355 } else {
2356 fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
2357 threadId, currentTime, action, pStack->threadStartTime,
2358 method->recursiveEntries,
2359 pStack->top, method->className);
2360 }
2361 #endif
2362
2363 if (action == METHOD_TRACE_ENTER) {
2364 /* This is a method entry */
2365 if (pStack->top >= MAX_STACK_DEPTH) {
2366 fprintf(stderr, "Stack overflow (exceeded %d frames)\n",
2367 MAX_STACK_DEPTH);
2368 exit(1);
2369 }
2370
2371 /* Get the caller method */
2372 if (pStack->top >= 1)
2373 caller = pStack->calls[pStack->top - 1].method;
2374 else
2375 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2376 countRecursiveEntries(pStack, pStack->top, caller);
2377 caller->elapsedExclusive += currentTime - pStack->lastEventTime;
2378 #if 0
2379 if (caller->elapsedExclusive > 10000000)
2380 fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n",
2381 caller->elapsedExclusive, currentTime,
2382 pStack->lastEventTime,
2383 currentTime - pStack->lastEventTime);
2384 #endif
2385 if (caller->recursiveEntries <= 1) {
2386 caller->topExclusive += currentTime - pStack->lastEventTime;
2387 }
2388
2389 /* Push the method on the stack for this thread */
2390 pStack->calls[pStack->top].method = method;
2391 pStack->calls[pStack->top++].entryTime = currentTime;
2392 } else {
2393 /* This is a method exit */
2394 uint64_t entryTime = 0;
2395
2396 /* Pop the method off the stack for this thread */
2397 if (pStack->top > 0) {
2398 pStack->top -= 1;
2399 entryTime = pStack->calls[pStack->top].entryTime;
2400 if (method != pStack->calls[pStack->top].method) {
2401 if (method->methodName) {
2402 fprintf(stderr,
2403 "Exit from method %s.%s %s does not match stack:\n",
2404 method->className, method->methodName,
2405 method->signature);
2406 } else {
2407 fprintf(stderr,
2408 "Exit from method %s does not match stack:\n",
2409 method->className);
2410 }
2411 stackDump(pStack, pStack->top + 1);
2412 exit(1);
2413 }
2414 }
2415
2416 /* Get the caller method */
2417 if (pStack->top >= 1)
2418 caller = pStack->calls[pStack->top - 1].method;
2419 else
2420 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2421 countRecursiveEntries(pStack, pStack->top, caller);
2422 countRecursiveEntries(pStack, pStack->top, method);
2423 uint64_t elapsed = currentTime - entryTime;
2424 addInclusiveTime(caller, method, elapsed);
2425 method->elapsedExclusive += currentTime - pStack->lastEventTime;
2426 if (method->recursiveEntries == 0) {
2427 method->topExclusive += currentTime - pStack->lastEventTime;
2428 }
2429 }
2430 /* Remember the time of the last entry or exit event */
2431 pStack->lastEventTime = currentTime;
2432 }
2433
2434 /* If we have calls on the stack when the trace ends, then clean
2435 * up the stack and add time to the callers by pretending that we
2436 * are exiting from their methods now.
2437 */
2438 CallStack *pStack;
2439 int threadId;
2440 uint64_t sumThreadTime = 0;
2441 for (threadId = 0; threadId < MAX_THREADS; ++threadId) {
2442 pStack = traceData->stacks[threadId];
2443
2444 /* If this thread never existed, then continue with next thread */
2445 if (pStack == NULL)
2446 continue;
2447
2448 /* Also, add up the time taken by all of the threads */
2449 sumThreadTime += pStack->lastEventTime - pStack->threadStartTime;
2450
2451 for (ii = 0; ii < pStack->top; ++ii) {
2452 if (ii == 0)
2453 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2454 else
2455 caller = pStack->calls[ii - 1].method;
2456 method = pStack->calls[ii].method;
2457 countRecursiveEntries(pStack, ii, caller);
2458 countRecursiveEntries(pStack, ii, method);
2459
2460 uint64_t entryTime = pStack->calls[ii].entryTime;
2461 uint64_t elapsed = pStack->lastEventTime - entryTime;
2462 addInclusiveTime(caller, method, elapsed);
2463 }
2464 }
2465 caller = &dataKeys->methods[TOPLEVEL_INDEX];
2466 caller->elapsedInclusive = sumThreadTime;
2467
2468 #if 0
2469 fclose(dumpStream);
2470 #endif
2471
2472 if (threadTime != NULL) {
2473 *threadTime = sumThreadTime;
2474 }
2475
2476 bail:
2477 if (dataFp != NULL)
2478 fclose(dataFp);
2479
2480 return dataKeys;
2481 }
2482
2483 MethodEntry** parseMethodEntries(DataKeys* dataKeys)
2484 {
2485 int ii;
2486 /* Create a new array of pointers to the methods and sort the pointers
2487 * instead of the actual MethodEntry structs. We need to do this
2488 * because there are other lists that contain pointers to the
2489 * MethodEntry structs.
2490 */
2491 MethodEntry** pMethods = (MethodEntry**) malloc(sizeof(MethodEntry*) * dataKeys->numMethods);
2492 for (ii = 0; ii < dataKeys->numMethods; ++ii) {
2493 MethodEntry* entry = &dataKeys->methods[ii];
2494 pMethods[ii] = entry;
2495 }
2496
2497 return pMethods;
2498 }
2499
2500 /*
2501 * Produce a function profile from the following methods
2502 */
2503 void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime)
2504 {
2505 /* Print the html header, if necessary */
2506 if (gOptions.outputHtml) {
2507 printf(htmlHeader, gOptions.sortableUrl);
2508 outputTableOfContents();
2509 }
2510
2511 printExclusiveProfile(pMethods, numMethods, sumThreadTime);
2512 printInclusiveProfile(pMethods, numMethods, sumThreadTime);
2513
2514 createClassList(traceData, pMethods, numMethods);
2515 printClassProfiles(traceData, sumThreadTime);
2516
2517 createUniqueMethodList(traceData, pMethods, numMethods);
2518 printMethodProfiles(traceData, sumThreadTime);
2519
2520 if (gOptions.outputHtml) {
2521 printf("%s", htmlFooter);
2522 }
2523 }
2524
2525 int compareMethodNamesForDiff(const void *a, const void *b)
2526 {
2527 int result;
2528
2529 const MethodEntry *methodA = *(const MethodEntry**)a;
2530 const MethodEntry *methodB = *(const MethodEntry**)b;
2531 if (methodA->methodName == NULL || methodB->methodName == NULL) {
2532 return compareClassNames(a, b);
2533 }
2534 result = strcmp(methodA->methodName, methodB->methodName);
2535 if (result == 0) {
2536 result = strcmp(methodA->signature, methodB->signature);
2537 if (result == 0) {
2538 return strcmp(methodA->className, methodB->className);
2539 }
2540 }
2541 return result;
2542 }
2543
2544 int findMatch(MethodEntry** methods, int size, MethodEntry* matchThis)
2545 {
2546 int i;
2547
2548 for (i = 0 ; i < size ; i++) {
2549 MethodEntry* method = methods[i];
2550
2551 if (method != NULL && !compareMethodNamesForDiff(&method, &matchThis)) {
2552 // printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName,
2553 // method->className, method->methodName);
2554
2555 return i;
2556 /* if (!compareMethodNames(&method, &matchThis)) {
2557 return i;
2558 }
2559 */ }
2560 }
2561
2562 return -1;
2563 }
2564
2565 int compareDiffEntriesExculsive(const void *a, const void *b)
2566 {
2567 int result;
2568
2569 const DiffEntry* entryA = (const DiffEntry*)a;
2570 const DiffEntry* entryB = (const DiffEntry*)b;
2571
2572 if (entryA->differenceExclusive < entryB->differenceExclusive) {
2573 return 1;
2574 } else if (entryA->differenceExclusive > entryB->differenceExclusive) {
2575 return -1;
2576 }
2577
2578 return 0;
2579 }
2580
2581 int compareDiffEntriesInculsive(const void *a, const void *b)
2582 {
2583 int result;
2584
2585 const DiffEntry* entryA = (const DiffEntry*)a;
2586 const DiffEntry* entryB = (const DiffEntry*)b;
2587
2588 if (entryA->differenceInclusive < entryB->differenceInclusive) {
2589 return 1;
2590 } else if (entryA->differenceInclusive > entryB->differenceInclusive) {
2591 return -1;
2592 }
2593
2594 return 0;
2595 }
2596
2597 void printMissingMethod(MethodEntry* method)
2598 {
2599 char classBuf[HTML_BUFSIZE];
2600 char methodBuf[HTML_BUFSIZE];
2601 char* className;
2602 char* methodName;
2603
2604 className = htmlEscape(method->className, classBuf, HTML_BUFSIZE);
2605 methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE);
2606
2607 if (gOptions.outputHtml) printf("<tr><td>\n");
2608
2609 printf("%s.%s ", className, methodName);
2610 if (gOptions.outputHtml) printf("</td><td>");
2611
2612 printf("%lld ", method->elapsedExclusive);
2613 if (gOptions.outputHtml) printf("</td><td>");
2614
2615 printf("%lld ", method->elapsedInclusive);
2616 if (gOptions.outputHtml) printf("</td><td>");
2617
2618 printf("%d\n", method->numCalls[0]);
2619 if (gOptions.outputHtml) printf("</td><td>\n");
2620 }
2621
2622
2623 void createDiff(DataKeys* d1, uint64_t sum1, DataKeys* d2, uint64_t sum2)
2624 {
2625 MethodEntry** methods1 = parseMethodEntries(d1);
2626 MethodEntry** methods2 = parseMethodEntries(d2);
2627
2628 // sort and assign the indicies
2629 int i;
2630 qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
2631 for (i = 0; i < d1->numMethods; ++i) {
2632 methods1[i]->index = i;
2633 }
2634
2635 qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
2636 for (i = 0; i < d2->numMethods; ++i) {
2637 methods2[i]->index = i;
2638 }
2639
2640 int max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods;
2641 max++;
2642 DiffEntry* diffs = (DiffEntry*)malloc(max * sizeof(DiffEntry));
2643 memset(diffs, 0, max * sizeof(DiffEntry));
2644 DiffEntry* ptr = diffs;
2645
2646 // printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n", d1->numMethods, d2->numMethods);
2647
2648 int matches = 0;
2649
2650 for (i = 0 ; i < d1->numMethods ; i++) {
2651 int match = findMatch(methods2, d2->numMethods, methods1[i]);
2652 if (match >= 0) {
2653 ptr->method1 = methods1[i];
2654 ptr->method2 = methods2[match];
2655
2656 uint64_t e1 = ptr->method1->elapsedExclusive;
2657 uint64_t e2 = ptr->method2->elapsedExclusive;
2658 if (e1 > 0) {
2659 ptr->differenceExclusive = e2 - e1;
2660 ptr->differenceExclusivePercentage = ((double)e2 / (double)e1) * 100.0;
2661 }
2662
2663 uint64_t i1 = ptr->method1->elapsedInclusive;
2664 uint64_t i2 = ptr->method2->elapsedInclusive;
2665 if (i1 > 0) {
2666 ptr->differenceInclusive = i2 - i1;
2667 ptr->differenceInclusivePercentage = ((double)i2 / (double)i1) * 100.0;
2668 }
2669
2670 // clear these out so we don't find them again and we know which ones
2671 // we have left over
2672 methods1[i] = NULL;
2673 methods2[match] = NULL;
2674 ptr++;
2675
2676 matches++;
2677 }
2678 }
2679 ptr->method1 = NULL;
2680 ptr->method2 = NULL;
2681
2682 qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive);
2683 ptr = diffs;
2684
2685 if (gOptions.outputHtml) {
2686 printf(htmlHeader, gOptions.sortableUrl);
2687 printf("<h3>Table of Contents</h3>\n");
2688 printf("<ul>\n");
2689 printf("<li><a href='#exclusive'>Exclusive</a>\n");
2690 printf("<li><a href='#inclusive'>Inclusive</a>\n");
2691 printf("</ul>\n");
2692 printf("Run 1: %s<br>\n", gOptions.diffFileName);
2693 printf("Run 2: %s<br>\n", gOptions.traceFileName);
2694 printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n");
2695 printf(tableHeader, "exclusive_table");
2696 }
2697
2698 char classBuf[HTML_BUFSIZE];
2699 char methodBuf[HTML_BUFSIZE];
2700 char* className;
2701 char* methodName;
2702
2703 while (ptr->method1 != NULL && ptr->method2 != NULL) {
2704 if (gOptions.outputHtml) printf("<tr><td>\n");
2705
2706 className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
2707 methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
2708
2709 printf("%s.%s ", className, methodName);
2710 if (gOptions.outputHtml) printf("</td><td>");
2711
2712 printf("%lld ", ptr->method1->elapsedExclusive);
2713 if (gOptions.outputHtml) printf("</td><td>");
2714
2715 printf("%llu ", ptr->method2->elapsedExclusive);
2716 if (gOptions.outputHtml) printf("</td><td>");
2717
2718 printf("%lld ", ptr->differenceExclusive);
2719 if (gOptions.outputHtml) printf("</td><td>");
2720
2721 printf("%.2f\n", ptr->differenceExclusivePercentage);
2722 if (gOptions.outputHtml) printf("</td><td>\n");
2723
2724 printf("%d\n", ptr->method1->numCalls[0]);
2725 if (gOptions.outputHtml) printf("</td><td>\n");
2726
2727 printf("%d\n", ptr->method2->numCalls[0]);
2728 if (gOptions.outputHtml) printf("</td></tr>\n");
2729
2730 ptr++;
2731 }
2732
2733 if (gOptions.outputHtml) printf("</table>\n");
2734
2735 if (gOptions.outputHtml) {
2736 printf(htmlHeader, gOptions.sortableUrl);
2737 printf("Run 1: %s<br>\n", gOptions.diffFileName);
2738 printf("Run 2: %s<br>\n", gOptions.traceFileName);
2739 printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n");
2740 printf(tableHeader, "inclusive_table");
2741 }
2742
2743 qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive);
2744 ptr = diffs;
2745
2746 while (ptr->method1 != NULL && ptr->method2 != NULL) {
2747 if (gOptions.outputHtml) printf("<tr><td>\n");
2748
2749 className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
2750 methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
2751
2752 printf("%s.%s ", className, methodName);
2753 if (gOptions.outputHtml) printf("</td><td>");
2754
2755 printf("%lld ", ptr->method1->elapsedInclusive);
2756 if (gOptions.outputHtml) printf("</td><td>");
2757
2758 printf("%llu ", ptr->method2->elapsedInclusive);
2759 if (gOptions.outputHtml) printf("</td><td>");
2760
2761 printf("%lld ", ptr->differenceInclusive);
2762 if (gOptions.outputHtml) printf("</td><td>");
2763
2764 printf("%.2f\n", ptr->differenceInclusivePercentage);
2765 if (gOptions.outputHtml) printf("</td><td>\n");
2766
2767 printf("%d\n", ptr->method1->numCalls[0]);
2768 if (gOptions.outputHtml) printf("</td><td>\n");
2769
2770 printf("%d\n", ptr->method2->numCalls[0]);
2771 if (gOptions.outputHtml) printf("</td></tr>\n");
2772
2773 ptr++;
2774 }
2775
2776 if (gOptions.outputHtml) {
2777 printf("</table>\n");
2778 printf("<h3>Run 1 methods not found in Run 2</h3>");
2779 printf(tableHeaderMissing, "?");
2780 }
2781
2782 for (i = 0; i < d1->numMethods; ++i) {
2783 if (methods1[i] != NULL) {
2784 printMissingMethod(methods1[i]);
2785 }
2786 }
2787
2788 if (gOptions.outputHtml) {
2789 printf("</table>\n");
2790 printf("<h3>Run 2 methods not found in Run 1</h3>");
2791 printf(tableHeaderMissing, "?");
2792 }
2793
2794 for (i = 0; i < d2->numMethods; ++i) {
2795 if (methods2[i] != NULL) {
2796 printMissingMethod(methods2[i]);
2797 }
2798 }
2799
2800 if (gOptions.outputHtml) printf("</body></html\n");
2801 }
2802
2803 int usage(const char *program)
2804 {
2805 fprintf(stderr, "Copyright (C) 2006 The Android Open Source Project\n\n");
2806 fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] trace-file-name\n", program);
2807 fprintf(stderr, " -d trace-file-name - Diff with this trace\n");
2808 fprintf(stderr, " -g outfile - Write graph to 'outfile'\n");
2809 fprintf(stderr, " -k - When writing a graph, keep the intermediate DOT file\n");
2810 fprintf(stderr, " -h - Turn on HTML output\n");
2811 fprintf(stderr, " -o - Dump the dmtrace file instead of profiling\n");
2812 fprintf(stderr, " -s - URL base to where the sortable javascript file\n");
2813 fprintf(stderr, " -t threshold - Threshold percentage for including nodes in the graph\n");
2814 return 2;
2815 }
2816
2817 // Returns true if there was an error
2818 int parseOptions(int argc, char **argv)
2819 {
2820 while (1) {
2821 int opt = getopt(argc, argv, "d:hg:kos:t:");
2822 if (opt == -1)
2823 break;
2824 switch (opt) {
2825 case 'd':
2826 gOptions.diffFileName = optarg;
2827 break;
2828 case 'g':
2829 gOptions.graphFileName = optarg;
2830 break;
2831 case 'k':
2832 gOptions.keepDotFile = 1;
2833 break;
2834 case 'h':
2835 gOptions.outputHtml = 1;
2836 break;
2837 case 'o':
2838 gOptions.dump = 1;
2839 break;
2840 case 's':
2841 gOptions.sortableUrl = optarg;
2842 break;
2843 case 't':
2844 gOptions.threshold = atoi(optarg);
2845 break;
2846 default:
2847 return 1;
2848 }
2849 }
2850 return 0;
2851 }
2852
2853 /*
2854 * Parse args.
2855 */
2856 int main(int argc, char** argv)
2857 {
2858 gOptions.threshold = -1;
2859
2860 // Parse the options
2861 if (parseOptions(argc, argv) || argc - optind != 1)
2862 return usage(argv[0]);
2863
2864 gOptions.traceFileName = argv[optind];
2865
2866 if (gOptions.threshold < 0 || 100 <= gOptions.threshold) {
2867 gOptions.threshold = 20;
2868 }
2869
2870 if (gOptions.dump) {
2871 dumpTrace();
2872 return 0;
2873 }
2874
2875 uint64_t sumThreadTime = 0;
2876
2877 TraceData data1;
2878 DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName,
2879 &sumThreadTime);
2880 if (dataKeys == NULL) {
2881 fprintf(stderr, "Cannot read \"%s\".\n", gOptions.traceFileName);
2882 exit(1);
2883 }
2884
2885 if (gOptions.diffFileName != NULL) {
2886 uint64_t sum2;
2887 TraceData data2;
2888 DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2);
2889 if (d2 == NULL) {
2890 fprintf(stderr, "Cannot read \"%s\".\n", gOptions.diffFileName);
2891 exit(1);
2892 }
2893
2894 createDiff(d2, sum2, dataKeys, sumThreadTime);
2895
2896 freeDataKeys(d2);
2897 } else {
2898 MethodEntry** methods = parseMethodEntries(dataKeys);
2899 profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime);
2900 if (gOptions.graphFileName != NULL) {
2901 createInclusiveProfileGraphNew(dataKeys);
2902 }
2903 free(methods);
2904 }
2905
2906 freeDataKeys(dataKeys);
2907
2908 return 0;
2909 }
+0
-18
tools/dmtracedump/dmtracedump.pl less more
0 #!/usr/bin/perl
1
2 opendir(DIR, ".") || die "can't opendir $some_dir: $!";
3 @traces = grep { /.*\.dmtrace\.data/ } readdir(DIR);
4
5 foreach (@traces)
6 {
7 $input = $_;
8 $input =~ s/\.data$//;
9
10 $output = "$input.html";
11
12 print("dmtracedump -h -p $input > $output\n");
13 system("dmtracedump -h -p '$input' > '$output'");
14
15 }
16
17 closedir DIR;
+0
-11
tools/dmtracedump/dumpdir.sh less more
0 #!/bin/bash
1
2 FILES=`ls $1/*.data | sed "s/^\\(.*\\).data$/\\1/"`
3
4 mkdir -p $2
5
6 for F in $FILES
7 do
8 G=$2/`echo $F | sed "s/.*\\///g"`.html
9 dmtracedump -h -p $F > $G
10 done
1515
1616 include $(CLEAR_VARS)
1717 LOCAL_SRC_FILES := HprofConv.c
18 LOCAL_MODULE_TAGS := optional
18 LOCAL_MODULE_HOST_OS := darwin linux windows
1919 LOCAL_MODULE := hprof-conv
2020 include $(BUILD_HOST_EXECUTABLE)