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
65hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
66    bool writeHeader, bool directToDdms)
67{
68    memset(ctx, 0, sizeof (*ctx));
69
70    /*
71     * Have to do this here, because it must happen after we
72     * memset the struct (want to treat fileDataPtr/fileDataSize
73     * as read-only while the file is open).
74     */
75    FILE* fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
76    if (fp == NULL) {
77        /* not expected */
78        LOGE("hprof: open_memstream failed: %s\n", strerror(errno));
79        dvmAbort();
80    }
81
82    ctx->directToDdms = directToDdms;
83    ctx->fileName = fileName;
84    ctx->memFp = fp;
85    ctx->fd = fd;
86
87    ctx->curRec.allocLen = 128;
88    ctx->curRec.body = malloc(ctx->curRec.allocLen);
89//xxx check for/return an error
90
91    if (writeHeader) {
92        char magic[] = HPROF_MAGIC_STRING;
93        unsigned char buf[4];
94        struct timeval now;
95        u8 nowMs;
96
97        /* Write the file header.
98         *
99         * [u1]*: NUL-terminated magic string.
100         */
101        fwrite(magic, 1, sizeof(magic), fp);
102
103        /* u4: size of identifiers.  We're using addresses
104         *     as IDs, so make sure a pointer fits.
105         */
106        U4_TO_BUF_BE(buf, 0, sizeof(void *));
107        fwrite(buf, 1, sizeof(u4), fp);
108
109        /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
110         */
111        if (gettimeofday(&now, NULL) < 0) {
112            nowMs = 0;
113        } else {
114            nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
115        }
116
117        /* u4: high word of the 64-bit time.
118         */
119        U4_TO_BUF_BE(buf, 0, (u4)(nowMs >> 32));
120        fwrite(buf, 1, sizeof(u4), fp);
121
122        /* u4: low word of the 64-bit time.
123         */
124        U4_TO_BUF_BE(buf, 0, (u4)(nowMs & 0xffffffffULL));
125        fwrite(buf, 1, sizeof(u4), fp); //xxx fix the time
126    }
127}
128
129int
130hprofFlushRecord(hprof_record_t *rec, FILE *fp)
131{
132    if (rec->dirty) {
133        unsigned char headBuf[sizeof (u1) + 2 * sizeof (u4)];
134        int nb;
135
136        headBuf[0] = rec->tag;
137        U4_TO_BUF_BE(headBuf, 1, rec->time);
138        U4_TO_BUF_BE(headBuf, 5, rec->length);
139
140        nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
141        if (nb != sizeof(headBuf)) {
142            return UNIQUE_ERROR();
143        }
144        nb = fwrite(rec->body, 1, rec->length, fp);
145        if (nb != (int)rec->length) {
146            return UNIQUE_ERROR();
147        }
148
149        rec->dirty = false;
150    }
151//xxx if we used less than half (or whatever) of allocLen, shrink the buffer.
152
153    return 0;
154}
155
156int
157hprofFlushCurrentRecord(hprof_context_t *ctx)
158{
159    return hprofFlushRecord(&ctx->curRec, ctx->memFp);
160}
161
162int
163hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time)
164{
165    hprof_record_t *rec = &ctx->curRec;
166    int err;
167
168    err = hprofFlushRecord(rec, ctx->memFp);
169    if (err != 0) {
170        return err;
171    } else if (rec->dirty) {
172        return UNIQUE_ERROR();
173    }
174
175    rec->dirty = true;
176    rec->tag = tag;
177    rec->time = time;
178    rec->length = 0;
179
180    return 0;
181}
182
183static inline int
184guaranteeRecordAppend(hprof_record_t *rec, size_t nmore)
185{
186    size_t minSize;
187
188    minSize = rec->length + nmore;
189    if (minSize > rec->allocLen) {
190        unsigned char *newBody;
191        size_t newAllocLen;
192
193        newAllocLen = rec->allocLen * 2;
194        if (newAllocLen < minSize) {
195            newAllocLen = rec->allocLen + nmore + nmore/2;
196        }
197        newBody = realloc(rec->body, newAllocLen);
198        if (newBody != NULL) {
199            rec->body = newBody;
200            rec->allocLen = newAllocLen;
201        } else {
202//TODO: set an error flag so future ops will fail
203            return UNIQUE_ERROR();
204        }
205    }
206
207    assert(rec->length + nmore <= rec->allocLen);
208    return 0;
209}
210
211int
212hprofAddU1ListToRecord(hprof_record_t *rec, const u1 *values, size_t numValues)
213{
214    int err;
215
216    err = guaranteeRecordAppend(rec, numValues);
217    if (err != 0) {
218        return err;
219    }
220
221    memcpy(rec->body + rec->length, values, numValues);
222    rec->length += numValues;
223
224    return 0;
225}
226
227int
228hprofAddU1ToRecord(hprof_record_t *rec, u1 value)
229{
230    int err;
231
232    err = guaranteeRecordAppend(rec, 1);
233    if (err != 0) {
234        return err;
235    }
236
237    rec->body[rec->length++] = value;
238
239    return 0;
240}
241
242int
243hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str)
244{
245    /* The terminating NUL character is NOT written.
246     */
247//xxx don't do a strlen;  add and grow as necessary, until NUL
248    return hprofAddU1ListToRecord(rec, (const u1 *)str, strlen(str));
249}
250
251int
252hprofAddU2ListToRecord(hprof_record_t *rec, const u2 *values, size_t numValues)
253{
254    unsigned char *insert;
255    size_t i;
256    int err;
257
258    err = guaranteeRecordAppend(rec, numValues * 2);
259    if (err != 0) {
260        return err;
261    }
262
263//xxx this can be way smarter
264//xxx also, don't do this bytewise if aligned and on a matching-endian arch
265    insert = rec->body + rec->length;
266    for (i = 0; i < numValues; i++) {
267        U2_TO_BUF_BE(insert, 0, *values++);
268        insert += sizeof(*values);
269    }
270    rec->length += numValues * 2;
271
272    return 0;
273}
274
275int
276hprofAddU2ToRecord(hprof_record_t *rec, u2 value)
277{
278    return hprofAddU2ListToRecord(rec, &value, 1);
279}
280
281int
282hprofAddU4ListToRecord(hprof_record_t *rec, const u4 *values, size_t numValues)
283{
284    unsigned char *insert;
285    size_t i;
286    int err;
287
288    err = guaranteeRecordAppend(rec, numValues * 4);
289    if (err != 0) {
290        return err;
291    }
292
293//xxx this can be way smarter
294//xxx also, don't do this bytewise if aligned and on a matching-endian arch
295    insert = rec->body + rec->length;
296    for (i = 0; i < numValues; i++) {
297        U4_TO_BUF_BE(insert, 0, *values++);
298        insert += sizeof(*values);
299    }
300    rec->length += numValues * 4;
301
302    return 0;
303}
304
305int
306hprofAddU4ToRecord(hprof_record_t *rec, u4 value)
307{
308    return hprofAddU4ListToRecord(rec, &value, 1);
309}
310
311int
312hprofAddU8ListToRecord(hprof_record_t *rec, const u8 *values, size_t numValues)
313{
314    unsigned char *insert;
315    size_t i;
316    int err;
317
318    err = guaranteeRecordAppend(rec, numValues * 8);
319    if (err != 0) {
320        return err;
321    }
322
323//xxx this can be way smarter
324//xxx also, don't do this bytewise if aligned and on a matching-endian arch
325    insert = rec->body + rec->length;
326    for (i = 0; i < numValues; i++) {
327        U8_TO_BUF_BE(insert, 0, *values++);
328        insert += sizeof(*values);
329    }
330    rec->length += numValues * 8;
331
332    return 0;
333}
334
335int
336hprofAddU8ToRecord(hprof_record_t *rec, u8 value)
337{
338    return hprofAddU8ListToRecord(rec, &value, 1);
339}
340