1*055d4590SKeyi Gui /*
2*055d4590SKeyi Gui * Copyright (C) 2009 The Android Open Source Project
3*055d4590SKeyi Gui *
4*055d4590SKeyi Gui * Licensed under the Apache License, Version 2.0 (the "License");
5*055d4590SKeyi Gui * you may not use this file except in compliance with the License.
6*055d4590SKeyi Gui * You may obtain a copy of the License at
7*055d4590SKeyi Gui *
8*055d4590SKeyi Gui * http://www.apache.org/licenses/LICENSE-2.0
9*055d4590SKeyi Gui *
10*055d4590SKeyi Gui * Unless required by applicable law or agreed to in writing, software
11*055d4590SKeyi Gui * distributed under the License is distributed on an "AS IS" BASIS,
12*055d4590SKeyi Gui * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*055d4590SKeyi Gui * See the License for the specific language governing permissions and
14*055d4590SKeyi Gui * limitations under the License.
15*055d4590SKeyi Gui */
16*055d4590SKeyi Gui
17*055d4590SKeyi Gui /*
18*055d4590SKeyi Gui * Strip Android-specific records out of hprof data, back-converting from
19*055d4590SKeyi Gui * 1.0.3 to 1.0.2. This removes some useful information, but allows
20*055d4590SKeyi Gui * Android hprof data to be handled by widely-available tools (like "jhat").
21*055d4590SKeyi Gui */
22*055d4590SKeyi Gui #include <stdio.h>
23*055d4590SKeyi Gui #include <string.h>
24*055d4590SKeyi Gui #include <stdlib.h>
25*055d4590SKeyi Gui #include <stdint.h>
26*055d4590SKeyi Gui #include <errno.h>
27*055d4590SKeyi Gui #include <assert.h>
28*055d4590SKeyi Gui #include <unistd.h>
29*055d4590SKeyi Gui
30*055d4590SKeyi Gui //#define VERBOSE_DEBUG
31*055d4590SKeyi Gui #ifdef VERBOSE_DEBUG
32*055d4590SKeyi Gui # define DBUG(...) fprintf(stderr, __VA_ARGS__)
33*055d4590SKeyi Gui #else
34*055d4590SKeyi Gui # define DBUG(...)
35*055d4590SKeyi Gui #endif
36*055d4590SKeyi Gui
37*055d4590SKeyi Gui #ifndef FALSE
38*055d4590SKeyi Gui # define FALSE 0
39*055d4590SKeyi Gui # define TRUE (!FALSE)
40*055d4590SKeyi Gui #endif
41*055d4590SKeyi Gui
42*055d4590SKeyi Gui // An attribute to place on a parameter to a function, for example:
43*055d4590SKeyi Gui // int foo(int x ATTRIBUTE_UNUSED) { return 10; }
44*055d4590SKeyi Gui // to avoid compiler warnings.
45*055d4590SKeyi Gui #define ATTRIBUTE_UNUSED __attribute__((__unused__))
46*055d4590SKeyi Gui
47*055d4590SKeyi Gui typedef enum HprofBasicType {
48*055d4590SKeyi Gui HPROF_BASIC_OBJECT = 2,
49*055d4590SKeyi Gui HPROF_BASIC_BOOLEAN = 4,
50*055d4590SKeyi Gui HPROF_BASIC_CHAR = 5,
51*055d4590SKeyi Gui HPROF_BASIC_FLOAT = 6,
52*055d4590SKeyi Gui HPROF_BASIC_DOUBLE = 7,
53*055d4590SKeyi Gui HPROF_BASIC_BYTE = 8,
54*055d4590SKeyi Gui HPROF_BASIC_SHORT = 9,
55*055d4590SKeyi Gui HPROF_BASIC_INT = 10,
56*055d4590SKeyi Gui HPROF_BASIC_LONG = 11,
57*055d4590SKeyi Gui } HprofBasicType;
58*055d4590SKeyi Gui
59*055d4590SKeyi Gui typedef enum HprofTag {
60*055d4590SKeyi Gui /* tags we must handle specially */
61*055d4590SKeyi Gui HPROF_TAG_HEAP_DUMP = 0x0c,
62*055d4590SKeyi Gui HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1c,
63*055d4590SKeyi Gui } HprofTag;
64*055d4590SKeyi Gui
65*055d4590SKeyi Gui typedef enum HprofHeapTag {
66*055d4590SKeyi Gui /* 1.0.2 tags */
67*055d4590SKeyi Gui HPROF_ROOT_UNKNOWN = 0xff,
68*055d4590SKeyi Gui HPROF_ROOT_JNI_GLOBAL = 0x01,
69*055d4590SKeyi Gui HPROF_ROOT_JNI_LOCAL = 0x02,
70*055d4590SKeyi Gui HPROF_ROOT_JAVA_FRAME = 0x03,
71*055d4590SKeyi Gui HPROF_ROOT_NATIVE_STACK = 0x04,
72*055d4590SKeyi Gui HPROF_ROOT_STICKY_CLASS = 0x05,
73*055d4590SKeyi Gui HPROF_ROOT_THREAD_BLOCK = 0x06,
74*055d4590SKeyi Gui HPROF_ROOT_MONITOR_USED = 0x07,
75*055d4590SKeyi Gui HPROF_ROOT_THREAD_OBJECT = 0x08,
76*055d4590SKeyi Gui HPROF_CLASS_DUMP = 0x20,
77*055d4590SKeyi Gui HPROF_INSTANCE_DUMP = 0x21,
78*055d4590SKeyi Gui HPROF_OBJECT_ARRAY_DUMP = 0x22,
79*055d4590SKeyi Gui HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
80*055d4590SKeyi Gui
81*055d4590SKeyi Gui /* Android 1.0.3 tags */
82*055d4590SKeyi Gui HPROF_HEAP_DUMP_INFO = 0xfe,
83*055d4590SKeyi Gui HPROF_ROOT_INTERNED_STRING = 0x89,
84*055d4590SKeyi Gui HPROF_ROOT_FINALIZING = 0x8a,
85*055d4590SKeyi Gui HPROF_ROOT_DEBUGGER = 0x8b,
86*055d4590SKeyi Gui HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
87*055d4590SKeyi Gui HPROF_ROOT_VM_INTERNAL = 0x8d,
88*055d4590SKeyi Gui HPROF_ROOT_JNI_MONITOR = 0x8e,
89*055d4590SKeyi Gui HPROF_UNREACHABLE = 0x90, /* deprecated */
90*055d4590SKeyi Gui HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
91*055d4590SKeyi Gui } HprofHeapTag;
92*055d4590SKeyi Gui
93*055d4590SKeyi Gui typedef enum HprofHeapId {
94*055d4590SKeyi Gui HPROF_HEAP_DEFAULT = 0,
95*055d4590SKeyi Gui HPROF_HEAP_ZYGOTE = 'Z',
96*055d4590SKeyi Gui HPROF_HEAP_APP = 'A',
97*055d4590SKeyi Gui HPROF_HEAP_IMAGE = 'I',
98*055d4590SKeyi Gui } HprofHeapId;
99*055d4590SKeyi Gui
100*055d4590SKeyi Gui #define kIdentSize 4
101*055d4590SKeyi Gui #define kRecHdrLen 9
102*055d4590SKeyi Gui
103*055d4590SKeyi Gui #define kFlagAppOnly 1
104*055d4590SKeyi Gui
105*055d4590SKeyi Gui /*
106*055d4590SKeyi Gui * ===========================================================================
107*055d4590SKeyi Gui * Expanding buffer
108*055d4590SKeyi Gui * ===========================================================================
109*055d4590SKeyi Gui */
110*055d4590SKeyi Gui
111*055d4590SKeyi Gui /* simple struct */
112*055d4590SKeyi Gui typedef struct {
113*055d4590SKeyi Gui unsigned char* storage;
114*055d4590SKeyi Gui size_t curLen;
115*055d4590SKeyi Gui size_t maxLen;
116*055d4590SKeyi Gui } ExpandBuf;
117*055d4590SKeyi Gui
118*055d4590SKeyi Gui /*
119*055d4590SKeyi Gui * Create an ExpandBuf.
120*055d4590SKeyi Gui */
ebAlloc(void)121*055d4590SKeyi Gui static ExpandBuf* ebAlloc(void)
122*055d4590SKeyi Gui {
123*055d4590SKeyi Gui static const int kInitialSize = 64;
124*055d4590SKeyi Gui
125*055d4590SKeyi Gui ExpandBuf* newBuf = (ExpandBuf*) malloc(sizeof(ExpandBuf));
126*055d4590SKeyi Gui if (newBuf == NULL)
127*055d4590SKeyi Gui return NULL;
128*055d4590SKeyi Gui newBuf->storage = (unsigned char*) malloc(kInitialSize);
129*055d4590SKeyi Gui newBuf->curLen = 0;
130*055d4590SKeyi Gui newBuf->maxLen = kInitialSize;
131*055d4590SKeyi Gui
132*055d4590SKeyi Gui return newBuf;
133*055d4590SKeyi Gui }
134*055d4590SKeyi Gui
135*055d4590SKeyi Gui /*
136*055d4590SKeyi Gui * Release the storage associated with an ExpandBuf.
137*055d4590SKeyi Gui */
ebFree(ExpandBuf * pBuf)138*055d4590SKeyi Gui static void ebFree(ExpandBuf* pBuf)
139*055d4590SKeyi Gui {
140*055d4590SKeyi Gui if (pBuf != NULL) {
141*055d4590SKeyi Gui free(pBuf->storage);
142*055d4590SKeyi Gui free(pBuf);
143*055d4590SKeyi Gui }
144*055d4590SKeyi Gui }
145*055d4590SKeyi Gui
146*055d4590SKeyi Gui /*
147*055d4590SKeyi Gui * Return a pointer to the data buffer.
148*055d4590SKeyi Gui *
149*055d4590SKeyi Gui * The pointer may change as data is added to the buffer, so this value
150*055d4590SKeyi Gui * should not be cached.
151*055d4590SKeyi Gui */
ebGetBuffer(ExpandBuf * pBuf)152*055d4590SKeyi Gui static inline unsigned char* ebGetBuffer(ExpandBuf* pBuf)
153*055d4590SKeyi Gui {
154*055d4590SKeyi Gui return pBuf->storage;
155*055d4590SKeyi Gui }
156*055d4590SKeyi Gui
157*055d4590SKeyi Gui /*
158*055d4590SKeyi Gui * Get the amount of data currently in the buffer.
159*055d4590SKeyi Gui */
ebGetLength(ExpandBuf * pBuf)160*055d4590SKeyi Gui static inline size_t ebGetLength(ExpandBuf* pBuf)
161*055d4590SKeyi Gui {
162*055d4590SKeyi Gui return pBuf->curLen;
163*055d4590SKeyi Gui }
164*055d4590SKeyi Gui
165*055d4590SKeyi Gui /*
166*055d4590SKeyi Gui * Empty the buffer.
167*055d4590SKeyi Gui */
ebClear(ExpandBuf * pBuf)168*055d4590SKeyi Gui static void ebClear(ExpandBuf* pBuf)
169*055d4590SKeyi Gui {
170*055d4590SKeyi Gui pBuf->curLen = 0;
171*055d4590SKeyi Gui }
172*055d4590SKeyi Gui
173*055d4590SKeyi Gui /*
174*055d4590SKeyi Gui * Ensure that the buffer can hold at least "size" additional bytes.
175*055d4590SKeyi Gui */
ebEnsureCapacity(ExpandBuf * pBuf,int size)176*055d4590SKeyi Gui static int ebEnsureCapacity(ExpandBuf* pBuf, int size)
177*055d4590SKeyi Gui {
178*055d4590SKeyi Gui assert(size > 0);
179*055d4590SKeyi Gui
180*055d4590SKeyi Gui if (pBuf->curLen + size > pBuf->maxLen) {
181*055d4590SKeyi Gui int newSize = pBuf->curLen + size + 128; /* oversize slightly */
182*055d4590SKeyi Gui unsigned char* newStorage = realloc(pBuf->storage, newSize);
183*055d4590SKeyi Gui if (newStorage == NULL) {
184*055d4590SKeyi Gui fprintf(stderr, "ERROR: realloc failed on size=%d\n", newSize);
185*055d4590SKeyi Gui exit(1);
186*055d4590SKeyi Gui }
187*055d4590SKeyi Gui
188*055d4590SKeyi Gui pBuf->storage = newStorage;
189*055d4590SKeyi Gui pBuf->maxLen = newSize;
190*055d4590SKeyi Gui }
191*055d4590SKeyi Gui
192*055d4590SKeyi Gui assert(pBuf->curLen + size <= pBuf->maxLen);
193*055d4590SKeyi Gui return 0;
194*055d4590SKeyi Gui }
195*055d4590SKeyi Gui
196*055d4590SKeyi Gui /*
197*055d4590SKeyi Gui * Add data to the buffer after ensuring it can hold it.
198*055d4590SKeyi Gui */
ebAddData(ExpandBuf * pBuf,const void * data,size_t count)199*055d4590SKeyi Gui static int ebAddData(ExpandBuf* pBuf, const void* data, size_t count)
200*055d4590SKeyi Gui {
201*055d4590SKeyi Gui ebEnsureCapacity(pBuf, count);
202*055d4590SKeyi Gui memcpy(pBuf->storage + pBuf->curLen, data, count);
203*055d4590SKeyi Gui pBuf->curLen += count;
204*055d4590SKeyi Gui return 0;
205*055d4590SKeyi Gui }
206*055d4590SKeyi Gui
207*055d4590SKeyi Gui /*
208*055d4590SKeyi Gui * Read a NULL-terminated string from the input.
209*055d4590SKeyi Gui */
ebReadString(ExpandBuf * pBuf,FILE * in)210*055d4590SKeyi Gui static int ebReadString(ExpandBuf* pBuf, FILE* in)
211*055d4590SKeyi Gui {
212*055d4590SKeyi Gui int ic;
213*055d4590SKeyi Gui
214*055d4590SKeyi Gui do {
215*055d4590SKeyi Gui ebEnsureCapacity(pBuf, 1);
216*055d4590SKeyi Gui
217*055d4590SKeyi Gui ic = getc(in);
218*055d4590SKeyi Gui if (feof(in) || ferror(in)) {
219*055d4590SKeyi Gui fprintf(stderr, "ERROR: failed reading input\n");
220*055d4590SKeyi Gui return -1;
221*055d4590SKeyi Gui }
222*055d4590SKeyi Gui
223*055d4590SKeyi Gui pBuf->storage[pBuf->curLen++] = (unsigned char) ic;
224*055d4590SKeyi Gui } while (ic != 0);
225*055d4590SKeyi Gui
226*055d4590SKeyi Gui return 0;
227*055d4590SKeyi Gui }
228*055d4590SKeyi Gui
229*055d4590SKeyi Gui /*
230*055d4590SKeyi Gui * Read some data, adding it to the expanding buffer.
231*055d4590SKeyi Gui *
232*055d4590SKeyi Gui * This will ensure that the buffer has enough space to hold the new data
233*055d4590SKeyi Gui * (plus the previous contents).
234*055d4590SKeyi Gui */
ebReadData(ExpandBuf * pBuf,FILE * in,size_t count,int eofExpected)235*055d4590SKeyi Gui static int ebReadData(ExpandBuf* pBuf, FILE* in, size_t count, int eofExpected)
236*055d4590SKeyi Gui {
237*055d4590SKeyi Gui size_t actual;
238*055d4590SKeyi Gui
239*055d4590SKeyi Gui assert(count > 0);
240*055d4590SKeyi Gui
241*055d4590SKeyi Gui ebEnsureCapacity(pBuf, count);
242*055d4590SKeyi Gui actual = fread(pBuf->storage + pBuf->curLen, 1, count, in);
243*055d4590SKeyi Gui if (actual != count) {
244*055d4590SKeyi Gui if (eofExpected && feof(in) && !ferror(in)) {
245*055d4590SKeyi Gui /* return without reporting an error */
246*055d4590SKeyi Gui } else {
247*055d4590SKeyi Gui fprintf(stderr, "ERROR: read %zu of %zu bytes\n", actual, count);
248*055d4590SKeyi Gui return -1;
249*055d4590SKeyi Gui }
250*055d4590SKeyi Gui }
251*055d4590SKeyi Gui
252*055d4590SKeyi Gui pBuf->curLen += count;
253*055d4590SKeyi Gui assert(pBuf->curLen <= pBuf->maxLen);
254*055d4590SKeyi Gui
255*055d4590SKeyi Gui return 0;
256*055d4590SKeyi Gui }
257*055d4590SKeyi Gui
258*055d4590SKeyi Gui /*
259*055d4590SKeyi Gui * Write the data from the buffer. Resets the data count to zero.
260*055d4590SKeyi Gui */
ebWriteData(ExpandBuf * pBuf,FILE * out)261*055d4590SKeyi Gui static int ebWriteData(ExpandBuf* pBuf, FILE* out)
262*055d4590SKeyi Gui {
263*055d4590SKeyi Gui size_t actual;
264*055d4590SKeyi Gui
265*055d4590SKeyi Gui assert(pBuf->curLen > 0);
266*055d4590SKeyi Gui assert(pBuf->curLen <= pBuf->maxLen);
267*055d4590SKeyi Gui
268*055d4590SKeyi Gui actual = fwrite(pBuf->storage, 1, pBuf->curLen, out);
269*055d4590SKeyi Gui if (actual != pBuf->curLen) {
270*055d4590SKeyi Gui fprintf(stderr, "ERROR: write %zu of %zu bytes\n", actual, pBuf->curLen);
271*055d4590SKeyi Gui return -1;
272*055d4590SKeyi Gui }
273*055d4590SKeyi Gui
274*055d4590SKeyi Gui pBuf->curLen = 0;
275*055d4590SKeyi Gui
276*055d4590SKeyi Gui return 0;
277*055d4590SKeyi Gui }
278*055d4590SKeyi Gui
279*055d4590SKeyi Gui
280*055d4590SKeyi Gui /*
281*055d4590SKeyi Gui * ===========================================================================
282*055d4590SKeyi Gui * Hprof stuff
283*055d4590SKeyi Gui * ===========================================================================
284*055d4590SKeyi Gui */
285*055d4590SKeyi Gui
286*055d4590SKeyi Gui /*
287*055d4590SKeyi Gui * Get a 2-byte value, in big-endian order, from memory.
288*055d4590SKeyi Gui */
get2BE(const unsigned char * buf)289*055d4590SKeyi Gui static uint16_t get2BE(const unsigned char* buf)
290*055d4590SKeyi Gui {
291*055d4590SKeyi Gui uint16_t val;
292*055d4590SKeyi Gui
293*055d4590SKeyi Gui val = (buf[0] << 8) | buf[1];
294*055d4590SKeyi Gui return val;
295*055d4590SKeyi Gui }
296*055d4590SKeyi Gui
297*055d4590SKeyi Gui /*
298*055d4590SKeyi Gui * Get a 4-byte value, in big-endian order, from memory.
299*055d4590SKeyi Gui */
get4BE(const unsigned char * buf)300*055d4590SKeyi Gui static uint32_t get4BE(const unsigned char* buf)
301*055d4590SKeyi Gui {
302*055d4590SKeyi Gui uint32_t val;
303*055d4590SKeyi Gui
304*055d4590SKeyi Gui val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
305*055d4590SKeyi Gui return val;
306*055d4590SKeyi Gui }
307*055d4590SKeyi Gui
308*055d4590SKeyi Gui /*
309*055d4590SKeyi Gui * Set a 4-byte value, in big-endian order.
310*055d4590SKeyi Gui */
set4BE(unsigned char * buf,uint32_t val)311*055d4590SKeyi Gui static void set4BE(unsigned char* buf, uint32_t val)
312*055d4590SKeyi Gui {
313*055d4590SKeyi Gui buf[0] = val >> 24;
314*055d4590SKeyi Gui buf[1] = val >> 16;
315*055d4590SKeyi Gui buf[2] = val >> 8;
316*055d4590SKeyi Gui buf[3] = val;
317*055d4590SKeyi Gui }
318*055d4590SKeyi Gui
319*055d4590SKeyi Gui /*
320*055d4590SKeyi Gui * Get the size, in bytes, of one of the "basic types".
321*055d4590SKeyi Gui */
computeBasicLen(HprofBasicType basicType)322*055d4590SKeyi Gui static int computeBasicLen(HprofBasicType basicType)
323*055d4590SKeyi Gui {
324*055d4590SKeyi Gui static const int sizes[] = { -1, -1, 4, -1, 1, 2, 4, 8, 1, 2, 4, 8 };
325*055d4590SKeyi Gui static const size_t maxSize = sizeof(sizes) / sizeof(sizes[0]);
326*055d4590SKeyi Gui
327*055d4590SKeyi Gui assert(basicType >= 0);
328*055d4590SKeyi Gui if (basicType >= maxSize)
329*055d4590SKeyi Gui return -1;
330*055d4590SKeyi Gui return sizes[basicType];
331*055d4590SKeyi Gui }
332*055d4590SKeyi Gui
333*055d4590SKeyi Gui /*
334*055d4590SKeyi Gui * Compute the length of a HPROF_CLASS_DUMP block.
335*055d4590SKeyi Gui */
computeClassDumpLen(const unsigned char * origBuf,int len)336*055d4590SKeyi Gui static int computeClassDumpLen(const unsigned char* origBuf, int len)
337*055d4590SKeyi Gui {
338*055d4590SKeyi Gui const unsigned char* buf = origBuf;
339*055d4590SKeyi Gui int blockLen = 0;
340*055d4590SKeyi Gui int i, count;
341*055d4590SKeyi Gui
342*055d4590SKeyi Gui blockLen += kIdentSize * 7 + 8;
343*055d4590SKeyi Gui buf += blockLen;
344*055d4590SKeyi Gui len -= blockLen;
345*055d4590SKeyi Gui
346*055d4590SKeyi Gui if (len < 0)
347*055d4590SKeyi Gui return -1;
348*055d4590SKeyi Gui
349*055d4590SKeyi Gui count = get2BE(buf);
350*055d4590SKeyi Gui buf += 2;
351*055d4590SKeyi Gui len -= 2;
352*055d4590SKeyi Gui DBUG("CDL: 1st count is %d\n", count);
353*055d4590SKeyi Gui for (i = 0; i < count; i++) {
354*055d4590SKeyi Gui HprofBasicType basicType;
355*055d4590SKeyi Gui int basicLen;
356*055d4590SKeyi Gui
357*055d4590SKeyi Gui basicType = buf[2];
358*055d4590SKeyi Gui basicLen = computeBasicLen(basicType);
359*055d4590SKeyi Gui if (basicLen < 0) {
360*055d4590SKeyi Gui DBUG("ERROR: invalid basicType %d\n", basicType);
361*055d4590SKeyi Gui return -1;
362*055d4590SKeyi Gui }
363*055d4590SKeyi Gui
364*055d4590SKeyi Gui buf += 2 + 1 + basicLen;
365*055d4590SKeyi Gui len -= 2 + 1 + basicLen;
366*055d4590SKeyi Gui if (len < 0)
367*055d4590SKeyi Gui return -1;
368*055d4590SKeyi Gui }
369*055d4590SKeyi Gui
370*055d4590SKeyi Gui count = get2BE(buf);
371*055d4590SKeyi Gui buf += 2;
372*055d4590SKeyi Gui len -= 2;
373*055d4590SKeyi Gui DBUG("CDL: 2nd count is %d\n", count);
374*055d4590SKeyi Gui for (i = 0; i < count; i++) {
375*055d4590SKeyi Gui HprofBasicType basicType;
376*055d4590SKeyi Gui int basicLen;
377*055d4590SKeyi Gui
378*055d4590SKeyi Gui basicType = buf[kIdentSize];
379*055d4590SKeyi Gui basicLen = computeBasicLen(basicType);
380*055d4590SKeyi Gui if (basicLen < 0) {
381*055d4590SKeyi Gui fprintf(stderr, "ERROR: invalid basicType %d\n", basicType);
382*055d4590SKeyi Gui return -1;
383*055d4590SKeyi Gui }
384*055d4590SKeyi Gui
385*055d4590SKeyi Gui buf += kIdentSize + 1 + basicLen;
386*055d4590SKeyi Gui len -= kIdentSize + 1 + basicLen;
387*055d4590SKeyi Gui if (len < 0)
388*055d4590SKeyi Gui return -1;
389*055d4590SKeyi Gui }
390*055d4590SKeyi Gui
391*055d4590SKeyi Gui count = get2BE(buf);
392*055d4590SKeyi Gui buf += 2;
393*055d4590SKeyi Gui len -= 2;
394*055d4590SKeyi Gui DBUG("CDL: 3rd count is %d\n", count);
395*055d4590SKeyi Gui for (i = 0; i < count; i++) {
396*055d4590SKeyi Gui buf += kIdentSize + 1;
397*055d4590SKeyi Gui len -= kIdentSize + 1;
398*055d4590SKeyi Gui if (len < 0)
399*055d4590SKeyi Gui return -1;
400*055d4590SKeyi Gui }
401*055d4590SKeyi Gui
402*055d4590SKeyi Gui DBUG("Total class dump len: %d\n", buf - origBuf);
403*055d4590SKeyi Gui return buf - origBuf;
404*055d4590SKeyi Gui }
405*055d4590SKeyi Gui
406*055d4590SKeyi Gui /*
407*055d4590SKeyi Gui * Compute the length of a HPROF_INSTANCE_DUMP block.
408*055d4590SKeyi Gui */
computeInstanceDumpLen(const unsigned char * origBuf,int len ATTRIBUTE_UNUSED)409*055d4590SKeyi Gui static int computeInstanceDumpLen(const unsigned char* origBuf, int len ATTRIBUTE_UNUSED)
410*055d4590SKeyi Gui {
411*055d4590SKeyi Gui int extraCount = get4BE(origBuf + kIdentSize * 2 + 4);
412*055d4590SKeyi Gui return kIdentSize * 2 + 8 + extraCount;
413*055d4590SKeyi Gui }
414*055d4590SKeyi Gui
415*055d4590SKeyi Gui /*
416*055d4590SKeyi Gui * Compute the length of a HPROF_OBJECT_ARRAY_DUMP block.
417*055d4590SKeyi Gui */
computeObjectArrayDumpLen(const unsigned char * origBuf,int len ATTRIBUTE_UNUSED)418*055d4590SKeyi Gui static int computeObjectArrayDumpLen(const unsigned char* origBuf, int len ATTRIBUTE_UNUSED)
419*055d4590SKeyi Gui {
420*055d4590SKeyi Gui int arrayCount = get4BE(origBuf + kIdentSize + 4);
421*055d4590SKeyi Gui return kIdentSize * 2 + 8 + arrayCount * kIdentSize;
422*055d4590SKeyi Gui }
423*055d4590SKeyi Gui
424*055d4590SKeyi Gui /*
425*055d4590SKeyi Gui * Compute the length of a HPROF_PRIMITIVE_ARRAY_DUMP block.
426*055d4590SKeyi Gui */
computePrimitiveArrayDumpLen(const unsigned char * origBuf,int len ATTRIBUTE_UNUSED)427*055d4590SKeyi Gui static int computePrimitiveArrayDumpLen(const unsigned char* origBuf, int len ATTRIBUTE_UNUSED)
428*055d4590SKeyi Gui {
429*055d4590SKeyi Gui int arrayCount = get4BE(origBuf + kIdentSize + 4);
430*055d4590SKeyi Gui HprofBasicType basicType = origBuf[kIdentSize + 8];
431*055d4590SKeyi Gui int basicLen = computeBasicLen(basicType);
432*055d4590SKeyi Gui
433*055d4590SKeyi Gui return kIdentSize + 9 + arrayCount * basicLen;
434*055d4590SKeyi Gui }
435*055d4590SKeyi Gui
436*055d4590SKeyi Gui /*
437*055d4590SKeyi Gui * Crunch through a heap dump record, writing the original or converted
438*055d4590SKeyi Gui * data to "out".
439*055d4590SKeyi Gui */
processHeapDump(ExpandBuf * pBuf,FILE * out,int flags)440*055d4590SKeyi Gui static int processHeapDump(ExpandBuf* pBuf, FILE* out, int flags)
441*055d4590SKeyi Gui {
442*055d4590SKeyi Gui ExpandBuf* pOutBuf = ebAlloc();
443*055d4590SKeyi Gui unsigned char* origBuf = ebGetBuffer(pBuf);
444*055d4590SKeyi Gui unsigned char* buf = origBuf;
445*055d4590SKeyi Gui int len = ebGetLength(pBuf);
446*055d4590SKeyi Gui int result = -1;
447*055d4590SKeyi Gui int heapType = HPROF_HEAP_DEFAULT;
448*055d4590SKeyi Gui int heapIgnore = FALSE;
449*055d4590SKeyi Gui
450*055d4590SKeyi Gui pBuf = NULL; /* we just use the raw pointer from here forward */
451*055d4590SKeyi Gui
452*055d4590SKeyi Gui /* copy the original header to the output buffer */
453*055d4590SKeyi Gui if (ebAddData(pOutBuf, buf, kRecHdrLen) != 0)
454*055d4590SKeyi Gui goto bail;
455*055d4590SKeyi Gui
456*055d4590SKeyi Gui buf += kRecHdrLen; /* skip past record header */
457*055d4590SKeyi Gui len -= kRecHdrLen;
458*055d4590SKeyi Gui
459*055d4590SKeyi Gui while (len > 0) {
460*055d4590SKeyi Gui unsigned char subType = buf[0];
461*055d4590SKeyi Gui int justCopy = TRUE;
462*055d4590SKeyi Gui int subLen;
463*055d4590SKeyi Gui
464*055d4590SKeyi Gui DBUG("--- 0x%02x ", subType);
465*055d4590SKeyi Gui switch (subType) {
466*055d4590SKeyi Gui /* 1.0.2 types */
467*055d4590SKeyi Gui case HPROF_ROOT_UNKNOWN:
468*055d4590SKeyi Gui subLen = kIdentSize;
469*055d4590SKeyi Gui break;
470*055d4590SKeyi Gui case HPROF_ROOT_JNI_GLOBAL:
471*055d4590SKeyi Gui subLen = kIdentSize * 2;
472*055d4590SKeyi Gui break;
473*055d4590SKeyi Gui case HPROF_ROOT_JNI_LOCAL:
474*055d4590SKeyi Gui subLen = kIdentSize + 8;
475*055d4590SKeyi Gui break;
476*055d4590SKeyi Gui case HPROF_ROOT_JAVA_FRAME:
477*055d4590SKeyi Gui subLen = kIdentSize + 8;
478*055d4590SKeyi Gui break;
479*055d4590SKeyi Gui case HPROF_ROOT_NATIVE_STACK:
480*055d4590SKeyi Gui subLen = kIdentSize + 4;
481*055d4590SKeyi Gui break;
482*055d4590SKeyi Gui case HPROF_ROOT_STICKY_CLASS:
483*055d4590SKeyi Gui subLen = kIdentSize;
484*055d4590SKeyi Gui break;
485*055d4590SKeyi Gui case HPROF_ROOT_THREAD_BLOCK:
486*055d4590SKeyi Gui subLen = kIdentSize + 4;
487*055d4590SKeyi Gui break;
488*055d4590SKeyi Gui case HPROF_ROOT_MONITOR_USED:
489*055d4590SKeyi Gui subLen = kIdentSize;
490*055d4590SKeyi Gui break;
491*055d4590SKeyi Gui case HPROF_ROOT_THREAD_OBJECT:
492*055d4590SKeyi Gui subLen = kIdentSize + 8;
493*055d4590SKeyi Gui break;
494*055d4590SKeyi Gui case HPROF_CLASS_DUMP:
495*055d4590SKeyi Gui subLen = computeClassDumpLen(buf+1, len-1);
496*055d4590SKeyi Gui break;
497*055d4590SKeyi Gui case HPROF_INSTANCE_DUMP:
498*055d4590SKeyi Gui subLen = computeInstanceDumpLen(buf+1, len-1);
499*055d4590SKeyi Gui if (heapIgnore) {
500*055d4590SKeyi Gui justCopy = FALSE;
501*055d4590SKeyi Gui }
502*055d4590SKeyi Gui break;
503*055d4590SKeyi Gui case HPROF_OBJECT_ARRAY_DUMP:
504*055d4590SKeyi Gui subLen = computeObjectArrayDumpLen(buf+1, len-1);
505*055d4590SKeyi Gui if (heapIgnore) {
506*055d4590SKeyi Gui justCopy = FALSE;
507*055d4590SKeyi Gui }
508*055d4590SKeyi Gui break;
509*055d4590SKeyi Gui case HPROF_PRIMITIVE_ARRAY_DUMP:
510*055d4590SKeyi Gui subLen = computePrimitiveArrayDumpLen(buf+1, len-1);
511*055d4590SKeyi Gui if (heapIgnore) {
512*055d4590SKeyi Gui justCopy = FALSE;
513*055d4590SKeyi Gui }
514*055d4590SKeyi Gui break;
515*055d4590SKeyi Gui /* these were added for Android in 1.0.3 */
516*055d4590SKeyi Gui case HPROF_HEAP_DUMP_INFO:
517*055d4590SKeyi Gui heapType = get4BE(buf+1);
518*055d4590SKeyi Gui if ((flags & kFlagAppOnly) != 0
519*055d4590SKeyi Gui && (heapType == HPROF_HEAP_ZYGOTE || heapType == HPROF_HEAP_IMAGE)) {
520*055d4590SKeyi Gui heapIgnore = TRUE;
521*055d4590SKeyi Gui } else {
522*055d4590SKeyi Gui heapIgnore = FALSE;
523*055d4590SKeyi Gui }
524*055d4590SKeyi Gui justCopy = FALSE;
525*055d4590SKeyi Gui subLen = kIdentSize + 4;
526*055d4590SKeyi Gui // no 1.0.2 equivalent for this
527*055d4590SKeyi Gui break;
528*055d4590SKeyi Gui case HPROF_ROOT_INTERNED_STRING:
529*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
530*055d4590SKeyi Gui subLen = kIdentSize;
531*055d4590SKeyi Gui break;
532*055d4590SKeyi Gui case HPROF_ROOT_FINALIZING:
533*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
534*055d4590SKeyi Gui subLen = kIdentSize;
535*055d4590SKeyi Gui break;
536*055d4590SKeyi Gui case HPROF_ROOT_DEBUGGER:
537*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
538*055d4590SKeyi Gui subLen = kIdentSize;
539*055d4590SKeyi Gui break;
540*055d4590SKeyi Gui case HPROF_ROOT_REFERENCE_CLEANUP:
541*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
542*055d4590SKeyi Gui subLen = kIdentSize;
543*055d4590SKeyi Gui break;
544*055d4590SKeyi Gui case HPROF_ROOT_VM_INTERNAL:
545*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
546*055d4590SKeyi Gui subLen = kIdentSize;
547*055d4590SKeyi Gui break;
548*055d4590SKeyi Gui case HPROF_ROOT_JNI_MONITOR:
549*055d4590SKeyi Gui /* keep the ident, drop the next 8 bytes */
550*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
551*055d4590SKeyi Gui justCopy = FALSE;
552*055d4590SKeyi Gui ebAddData(pOutBuf, buf, 1 + kIdentSize);
553*055d4590SKeyi Gui subLen = kIdentSize + 8;
554*055d4590SKeyi Gui break;
555*055d4590SKeyi Gui case HPROF_UNREACHABLE:
556*055d4590SKeyi Gui buf[0] = HPROF_ROOT_UNKNOWN;
557*055d4590SKeyi Gui subLen = kIdentSize;
558*055d4590SKeyi Gui break;
559*055d4590SKeyi Gui case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
560*055d4590SKeyi Gui buf[0] = HPROF_PRIMITIVE_ARRAY_DUMP;
561*055d4590SKeyi Gui buf[5] = buf[6] = buf[7] = buf[8] = 0; /* set array len to 0 */
562*055d4590SKeyi Gui subLen = kIdentSize + 9;
563*055d4590SKeyi Gui break;
564*055d4590SKeyi Gui
565*055d4590SKeyi Gui /* shouldn't get here */
566*055d4590SKeyi Gui default:
567*055d4590SKeyi Gui fprintf(stderr, "ERROR: unexpected subtype 0x%02x at offset %zu\n",
568*055d4590SKeyi Gui subType, (size_t) (buf - origBuf));
569*055d4590SKeyi Gui goto bail;
570*055d4590SKeyi Gui }
571*055d4590SKeyi Gui
572*055d4590SKeyi Gui if (justCopy) {
573*055d4590SKeyi Gui /* copy source data */
574*055d4590SKeyi Gui DBUG("(%d)\n", 1 + subLen);
575*055d4590SKeyi Gui ebAddData(pOutBuf, buf, 1 + subLen);
576*055d4590SKeyi Gui } else {
577*055d4590SKeyi Gui /* other data has been written, or the sub-record omitted */
578*055d4590SKeyi Gui DBUG("(adv %d)\n", 1 + subLen);
579*055d4590SKeyi Gui }
580*055d4590SKeyi Gui
581*055d4590SKeyi Gui /* advance to next entry */
582*055d4590SKeyi Gui buf += 1 + subLen;
583*055d4590SKeyi Gui len -= 1 + subLen;
584*055d4590SKeyi Gui }
585*055d4590SKeyi Gui
586*055d4590SKeyi Gui /*
587*055d4590SKeyi Gui * Update the record length.
588*055d4590SKeyi Gui */
589*055d4590SKeyi Gui set4BE(ebGetBuffer(pOutBuf) + 5, ebGetLength(pOutBuf) - kRecHdrLen);
590*055d4590SKeyi Gui
591*055d4590SKeyi Gui if (ebWriteData(pOutBuf, out) != 0)
592*055d4590SKeyi Gui goto bail;
593*055d4590SKeyi Gui
594*055d4590SKeyi Gui result = 0;
595*055d4590SKeyi Gui
596*055d4590SKeyi Gui bail:
597*055d4590SKeyi Gui ebFree(pOutBuf);
598*055d4590SKeyi Gui return result;
599*055d4590SKeyi Gui }
600*055d4590SKeyi Gui
601*055d4590SKeyi Gui /*
602*055d4590SKeyi Gui * Filter an hprof data file.
603*055d4590SKeyi Gui */
filterData(FILE * in,FILE * out,int flags)604*055d4590SKeyi Gui static int filterData(FILE* in, FILE* out, int flags)
605*055d4590SKeyi Gui {
606*055d4590SKeyi Gui const char *magicString;
607*055d4590SKeyi Gui ExpandBuf* pBuf;
608*055d4590SKeyi Gui int result = -1;
609*055d4590SKeyi Gui
610*055d4590SKeyi Gui pBuf = ebAlloc();
611*055d4590SKeyi Gui if (pBuf == NULL)
612*055d4590SKeyi Gui goto bail;
613*055d4590SKeyi Gui
614*055d4590SKeyi Gui /*
615*055d4590SKeyi Gui * Start with the header.
616*055d4590SKeyi Gui */
617*055d4590SKeyi Gui if (ebReadString(pBuf, in) != 0)
618*055d4590SKeyi Gui goto bail;
619*055d4590SKeyi Gui
620*055d4590SKeyi Gui magicString = (const char*)ebGetBuffer(pBuf);
621*055d4590SKeyi Gui if (strcmp(magicString, "JAVA PROFILE 1.0.3") != 0) {
622*055d4590SKeyi Gui if (strcmp(magicString, "JAVA PROFILE 1.0.2") == 0) {
623*055d4590SKeyi Gui fprintf(stderr, "ERROR: HPROF file already in 1.0.2 format.\n");
624*055d4590SKeyi Gui } else {
625*055d4590SKeyi Gui fprintf(stderr, "ERROR: expecting HPROF file format 1.0.3\n");
626*055d4590SKeyi Gui }
627*055d4590SKeyi Gui goto bail;
628*055d4590SKeyi Gui }
629*055d4590SKeyi Gui
630*055d4590SKeyi Gui /* downgrade to 1.0.2 */
631*055d4590SKeyi Gui (ebGetBuffer(pBuf))[17] = '2';
632*055d4590SKeyi Gui if (ebWriteData(pBuf, out) != 0)
633*055d4590SKeyi Gui goto bail;
634*055d4590SKeyi Gui
635*055d4590SKeyi Gui /*
636*055d4590SKeyi Gui * Copy:
637*055d4590SKeyi Gui * (4b) identifier size, always 4
638*055d4590SKeyi Gui * (8b) file creation date
639*055d4590SKeyi Gui */
640*055d4590SKeyi Gui if (ebReadData(pBuf, in, 12, FALSE) != 0)
641*055d4590SKeyi Gui goto bail;
642*055d4590SKeyi Gui if (ebWriteData(pBuf, out) != 0)
643*055d4590SKeyi Gui goto bail;
644*055d4590SKeyi Gui
645*055d4590SKeyi Gui /*
646*055d4590SKeyi Gui * Read records until we hit EOF. Each record begins with:
647*055d4590SKeyi Gui * (1b) type
648*055d4590SKeyi Gui * (4b) timestamp
649*055d4590SKeyi Gui * (4b) length of data that follows
650*055d4590SKeyi Gui */
651*055d4590SKeyi Gui while (1) {
652*055d4590SKeyi Gui assert(ebGetLength(pBuf) == 0);
653*055d4590SKeyi Gui
654*055d4590SKeyi Gui /* read type char */
655*055d4590SKeyi Gui if (ebReadData(pBuf, in, 1, TRUE) != 0)
656*055d4590SKeyi Gui goto bail;
657*055d4590SKeyi Gui if (feof(in))
658*055d4590SKeyi Gui break;
659*055d4590SKeyi Gui
660*055d4590SKeyi Gui /* read the rest of the header */
661*055d4590SKeyi Gui if (ebReadData(pBuf, in, kRecHdrLen-1, FALSE) != 0)
662*055d4590SKeyi Gui goto bail;
663*055d4590SKeyi Gui
664*055d4590SKeyi Gui unsigned char* buf = ebGetBuffer(pBuf);
665*055d4590SKeyi Gui unsigned char type;
666*055d4590SKeyi Gui unsigned int timestamp ATTRIBUTE_UNUSED;
667*055d4590SKeyi Gui unsigned int length;
668*055d4590SKeyi Gui
669*055d4590SKeyi Gui type = buf[0];
670*055d4590SKeyi Gui timestamp = get4BE(buf + 1);
671*055d4590SKeyi Gui length = get4BE(buf + 5);
672*055d4590SKeyi Gui buf = NULL; /* ptr invalid after next read op */
673*055d4590SKeyi Gui
674*055d4590SKeyi Gui /* read the record data */
675*055d4590SKeyi Gui if (length != 0) {
676*055d4590SKeyi Gui if (ebReadData(pBuf, in, length, FALSE) != 0)
677*055d4590SKeyi Gui goto bail;
678*055d4590SKeyi Gui }
679*055d4590SKeyi Gui
680*055d4590SKeyi Gui if (type == HPROF_TAG_HEAP_DUMP
681*055d4590SKeyi Gui || type == HPROF_TAG_HEAP_DUMP_SEGMENT) {
682*055d4590SKeyi Gui DBUG("Processing heap dump 0x%02x (%d bytes)\n",
683*055d4590SKeyi Gui type, length);
684*055d4590SKeyi Gui if (processHeapDump(pBuf, out, flags) != 0)
685*055d4590SKeyi Gui goto bail;
686*055d4590SKeyi Gui ebClear(pBuf);
687*055d4590SKeyi Gui } else {
688*055d4590SKeyi Gui /* keep */
689*055d4590SKeyi Gui DBUG("Keeping 0x%02x (%d bytes)\n", type, length);
690*055d4590SKeyi Gui if (ebWriteData(pBuf, out) != 0)
691*055d4590SKeyi Gui goto bail;
692*055d4590SKeyi Gui }
693*055d4590SKeyi Gui }
694*055d4590SKeyi Gui
695*055d4590SKeyi Gui result = 0;
696*055d4590SKeyi Gui
697*055d4590SKeyi Gui bail:
698*055d4590SKeyi Gui ebFree(pBuf);
699*055d4590SKeyi Gui return result;
700*055d4590SKeyi Gui }
701*055d4590SKeyi Gui
fopen_or_default(const char * path,const char * mode,FILE * def)702*055d4590SKeyi Gui static FILE* fopen_or_default(const char* path, const char* mode, FILE* def) {
703*055d4590SKeyi Gui if (!strcmp(path, "-")) {
704*055d4590SKeyi Gui return def;
705*055d4590SKeyi Gui } else {
706*055d4590SKeyi Gui return fopen(path, mode);
707*055d4590SKeyi Gui }
708*055d4590SKeyi Gui }
709*055d4590SKeyi Gui
main(int argc,char ** argv)710*055d4590SKeyi Gui int main(int argc, char** argv)
711*055d4590SKeyi Gui {
712*055d4590SKeyi Gui FILE* in = NULL;
713*055d4590SKeyi Gui FILE* out = NULL;
714*055d4590SKeyi Gui int flags = 0;
715*055d4590SKeyi Gui int res = 1;
716*055d4590SKeyi Gui
717*055d4590SKeyi Gui int opt;
718*055d4590SKeyi Gui while ((opt = getopt(argc, argv, "z")) != -1) {
719*055d4590SKeyi Gui switch (opt) {
720*055d4590SKeyi Gui case 'z':
721*055d4590SKeyi Gui flags |= kFlagAppOnly;
722*055d4590SKeyi Gui break;
723*055d4590SKeyi Gui case '?':
724*055d4590SKeyi Gui default:
725*055d4590SKeyi Gui goto usage;
726*055d4590SKeyi Gui }
727*055d4590SKeyi Gui }
728*055d4590SKeyi Gui
729*055d4590SKeyi Gui int i;
730*055d4590SKeyi Gui for (i = optind; i < argc; i++) {
731*055d4590SKeyi Gui char* arg = argv[i];
732*055d4590SKeyi Gui if (!in) {
733*055d4590SKeyi Gui in = fopen_or_default(arg, "rb", stdin);
734*055d4590SKeyi Gui } else if (!out) {
735*055d4590SKeyi Gui out = fopen_or_default(arg, "wb", stdout);
736*055d4590SKeyi Gui } else {
737*055d4590SKeyi Gui goto usage;
738*055d4590SKeyi Gui }
739*055d4590SKeyi Gui }
740*055d4590SKeyi Gui
741*055d4590SKeyi Gui if (in == NULL || out == NULL) {
742*055d4590SKeyi Gui goto usage;
743*055d4590SKeyi Gui }
744*055d4590SKeyi Gui
745*055d4590SKeyi Gui res = filterData(in, out, flags);
746*055d4590SKeyi Gui goto finish;
747*055d4590SKeyi Gui
748*055d4590SKeyi Gui usage:
749*055d4590SKeyi Gui fprintf(stderr, "Usage: hprof-conf [-z] infile outfile\n");
750*055d4590SKeyi Gui fprintf(stderr, "\n");
751*055d4590SKeyi Gui fprintf(stderr, " -z: exclude non-app heaps, such as Zygote\n");
752*055d4590SKeyi Gui fprintf(stderr, "\n");
753*055d4590SKeyi Gui fprintf(stderr, "Specify '-' for either or both files to use stdin/stdout.\n");
754*055d4590SKeyi Gui fprintf(stderr, "\n");
755*055d4590SKeyi Gui
756*055d4590SKeyi Gui fprintf(stderr,
757*055d4590SKeyi Gui "Copyright (C) 2009 The Android Open Source Project\n\n"
758*055d4590SKeyi Gui "This software is built from source code licensed under the "
759*055d4590SKeyi Gui "Apache License,\n"
760*055d4590SKeyi Gui "Version 2.0 (the \"License\"). You may obtain a copy of the "
761*055d4590SKeyi Gui "License at\n\n"
762*055d4590SKeyi Gui " http://www.apache.org/licenses/LICENSE-2.0\n\n"
763*055d4590SKeyi Gui "See the associated NOTICE file for this software for further "
764*055d4590SKeyi Gui "details.\n");
765*055d4590SKeyi Gui res = 2;
766*055d4590SKeyi Gui
767*055d4590SKeyi Gui finish:
768*055d4590SKeyi Gui if (in != stdin && in != NULL)
769*055d4590SKeyi Gui fclose(in);
770*055d4590SKeyi Gui if (out != stdout && out != NULL)
771*055d4590SKeyi Gui fclose(out);
772*055d4590SKeyi Gui return res;
773*055d4590SKeyi Gui }
774