xref: /aosp_15_r20/dalvik/tools/hprof-conv/HprofConv.c (revision 055d459012065f78d96b68be8421640240ddf631)
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