HprofOutput.cpp revision c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2ef
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <sys/time.h>
17#include <cutils/open_memstream.h>
18#include <time.h>
19#include <errno.h>
20#include "Hprof.h"
21
22#define HPROF_MAGIC_STRING  "JAVA PROFILE 1.0.3"
23
24#define U2_TO_BUF_BE(buf, offset, value) \
25    do { \
26        unsigned char *buf_ = (unsigned char *)(buf); \
27        int offset_ = (int)(offset); \
28        u2 value_ = (u2)(value); \
29        buf_[offset_ + 0] = (unsigned char)(value_ >>  8); \
30        buf_[offset_ + 1] = (unsigned char)(value_      ); \
31    } while (0)
32
33#define U4_TO_BUF_BE(buf, offset, value) \
34    do { \
35        unsigned char *buf_ = (unsigned char *)(buf); \
36        int offset_ = (int)(offset); \
37        u4 value_ = (u4)(value); \
38        buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
39        buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
40        buf_[offset_ + 2] = (unsigned char)(value_ >>  8); \
41        buf_[offset_ + 3] = (unsigned char)(value_      ); \
42    } while (0)
43
44#define U8_TO_BUF_BE(buf, offset, value) \
45    do { \
46        unsigned char *buf_ = (unsigned char *)(buf); \
47        int offset_ = (int)(offset); \
48        u8 value_ = (u8)(value); \
49        buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
50        buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
51        buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
52        buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
53        buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
54        buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
55        buf_[offset_ + 6] = (unsigned char)(value_ >>  8); \
56        buf_[offset_ + 7] = (unsigned char)(value_      ); \
57    } while (0)
58
59/*
60 * Initialize an hprof context struct.
61 *
62 * This will take ownership of "fileName".
63 */
64void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
65                      bool writeHeader, bool directToDdms)
66{
67    memset(ctx, 0, sizeof (*ctx));
68
69    /*
70     * Have to do this here, because it must happen after we
71     * memset the struct (want to treat fileDataPtr/fileDataSize
72     * as read-only while the file is open).
73     */
74    FILE* fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
75    if (fp == NULL) {
76        /* not expected */
77        ALOGE("hprof: open_memstream failed: %s", strerror(errno));
78        dvmAbort();
79    }
80
81    ctx->directToDdms = directToDdms;
82    ctx->fileName = fileName;
83    ctx->memFp = fp;
84    ctx->fd = fd;
85
86    ctx->curRec.allocLen = 128;
87    ctx->curRec.body = (unsigned char *)malloc(ctx->curRec.allocLen);
88//xxx check for/return an error
89
90    if (writeHeader) {
91        char magic[] = HPROF_MAGIC_STRING;
92        unsigned char buf[4];
93        struct timeval now;
94        u8 nowMs;
95
96        /* Write the file header.
97         *
98         * [u1]*: NUL-terminated magic string.
99         */
100        fwrite(magic, 1, sizeof(magic), fp);
101
102        /* u4: size of identifiers.  We're using addresses
103         *     as IDs, so make sure a pointer fits.
104         */
105        U4_TO_BUF_BE(buf, 0, sizeof(void *));
106        fwrite(buf, 1, sizeof(u4), fp);
107
108        /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
109         */
110        if (gettimeofday(&now, NULL) < 0) {
111            nowMs = 0;
112        } else {
113            nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
114        }
115
116        /* u4: high word of the 64-bit time.
117         */
118        U4_TO_BUF_BE(buf, 0, (u4)(nowMs >> 32));
119        fwrite(buf, 1, sizeof(u4), fp);
120
121        /* u4: low word of the 64-bit time.
122         */
123        U4_TO_BUF_BE(buf, 0, (u4)(nowMs & 0xffffffffULL));
124        fwrite(buf, 1, sizeof(u4), fp); //xxx fix the time
125    }
126}
127
128int hprofFlushRecord(hprof_record_t *rec, FILE *fp)
129{
130    if (rec->dirty) {
131        unsigned char headBuf[sizeof (u1) + 2 * sizeof (u4)];
132        int nb;
133
134        headBuf[0] = rec->tag;
135        U4_TO_BUF_BE(headBuf, 1, rec->time);
136        U4_TO_BUF_BE(headBuf, 5, rec->length);
137
138        nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
139        if (nb != sizeof(headBuf)) {
140            return UNIQUE_ERROR();
141        }
142        nb = fwrite(rec->body, 1, rec->length, fp);
143        if (nb != (int)rec->length) {
144            return UNIQUE_ERROR();
145        }
146
147        rec->dirty = false;
148    }
149//xxx if we used less than half (or whatever) of allocLen, shrink the buffer.
150
151    return 0;
152}
153
154int hprofFlushCurrentRecord(hprof_context_t *ctx)
155{
156    return hprofFlushRecord(&ctx->curRec, ctx->memFp);
157}
158
159int hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time)
160{
161    hprof_record_t *rec = &ctx->curRec;
162    int err;
163
164    err = hprofFlushRecord(rec, ctx->memFp);
165    if (err != 0) {
166        return err;
167    } else if (rec->dirty) {
168        return UNIQUE_ERROR();
169    }
170
171    rec->dirty = true;
172    rec->tag = tag;
173    rec->time = time;
174    rec->length = 0;
175
176    return 0;
177}
178
179static inline int guaranteeRecordAppend(hprof_record_t *rec, size_t nmore)
180{
181    size_t minSize;
182
183    minSize = rec->length + nmore;
184    if (minSize > rec->allocLen) {
185        unsigned char *newBody;
186        size_t newAllocLen;
187
188        newAllocLen = rec->allocLen * 2;
189        if (newAllocLen < minSize) {
190            newAllocLen = rec->allocLen + nmore + nmore/2;
191        }
192        newBody = (unsigned char *)realloc(rec->body, newAllocLen);
193        if (newBody != NULL) {
194            rec->body = newBody;
195            rec->allocLen = newAllocLen;
196        } else {
197//TODO: set an error flag so future ops will fail
198            return UNIQUE_ERROR();
199        }
200    }
201
202    assert(rec->length + nmore <= rec->allocLen);
203    return 0;
204}
205
206int hprofAddU1ListToRecord(hprof_record_t *rec, const u1 *values,
207                           size_t numValues)
208{
209    int err;
210
211    err = guaranteeRecordAppend(rec, numValues);
212    if (err != 0) {
213        return err;
214    }
215
216    memcpy(rec->body + rec->length, values, numValues);
217    rec->length += numValues;
218
219    return 0;
220}
221
222int hprofAddU1ToRecord(hprof_record_t *rec, u1 value)
223{
224    int err;
225
226    err = guaranteeRecordAppend(rec, 1);
227    if (err != 0) {
228        return err;
229    }
230
231    rec->body[rec->length++] = value;
232
233    return 0;
234}
235
236int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str)
237{
238    /* The terminating NUL character is NOT written.
239     */
240//xxx don't do a strlen;  add and grow as necessary, until NUL
241    return hprofAddU1ListToRecord(rec, (const u1 *)str, strlen(str));
242}
243
244int hprofAddU2ListToRecord(hprof_record_t *rec, const u2 *values,
245                           size_t numValues)
246{
247    int err = guaranteeRecordAppend(rec, numValues * 2);
248    if (err != 0) {
249        return err;
250    }
251
252//xxx this can be way smarter
253//xxx also, don't do this bytewise if aligned and on a matching-endian arch
254    unsigned char *insert = rec->body + rec->length;
255    for (size_t i = 0; i < numValues; i++) {
256        U2_TO_BUF_BE(insert, 0, *values++);
257        insert += sizeof(*values);
258    }
259    rec->length += numValues * 2;
260
261    return 0;
262}
263
264int hprofAddU2ToRecord(hprof_record_t *rec, u2 value)
265{
266    return hprofAddU2ListToRecord(rec, &value, 1);
267}
268
269int hprofAddU4ListToRecord(hprof_record_t *rec, const u4 *values,
270                           size_t numValues)
271{
272    int err = guaranteeRecordAppend(rec, numValues * 4);
273    if (err != 0) {
274        return err;
275    }
276
277//xxx this can be way smarter
278//xxx also, don't do this bytewise if aligned and on a matching-endian arch
279    unsigned char *insert = rec->body + rec->length;
280    for (size_t i = 0; i < numValues; i++) {
281        U4_TO_BUF_BE(insert, 0, *values++);
282        insert += sizeof(*values);
283    }
284    rec->length += numValues * 4;
285
286    return 0;
287}
288
289int hprofAddU4ToRecord(hprof_record_t *rec, u4 value)
290{
291    return hprofAddU4ListToRecord(rec, &value, 1);
292}
293
294int hprofAddU8ListToRecord(hprof_record_t *rec, const u8 *values,
295                           size_t numValues)
296{
297    int err = guaranteeRecordAppend(rec, numValues * 8);
298    if (err != 0) {
299        return err;
300    }
301
302//xxx this can be way smarter
303//xxx also, don't do this bytewise if aligned and on a matching-endian arch
304    unsigned char *insert = rec->body + rec->length;
305    for (size_t i = 0; i < numValues; i++) {
306        U8_TO_BUF_BE(insert, 0, *values++);
307        insert += sizeof(*values);
308    }
309    rec->length += numValues * 8;
310
311    return 0;
312}
313
314int hprofAddU8ToRecord(hprof_record_t *rec, u8 value)
315{
316    return hprofAddU8ListToRecord(rec, &value, 1);
317}
318