| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include <sys/time.h> |
| #include <cutils/open_memstream.h> |
| #include <time.h> |
| #include <errno.h> |
| #include "Hprof.h" |
| |
| #define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3" |
| |
| #define U2_TO_BUF_BE(buf, offset, value) \ |
| do { \ |
| unsigned char *buf_ = (unsigned char *)(buf); \ |
| int offset_ = (int)(offset); \ |
| u2 value_ = (u2)(value); \ |
| buf_[offset_ + 0] = (unsigned char)(value_ >> 8); \ |
| buf_[offset_ + 1] = (unsigned char)(value_ ); \ |
| } while (0) |
| |
| #define U4_TO_BUF_BE(buf, offset, value) \ |
| do { \ |
| unsigned char *buf_ = (unsigned char *)(buf); \ |
| int offset_ = (int)(offset); \ |
| u4 value_ = (u4)(value); \ |
| buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \ |
| buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \ |
| buf_[offset_ + 2] = (unsigned char)(value_ >> 8); \ |
| buf_[offset_ + 3] = (unsigned char)(value_ ); \ |
| } while (0) |
| |
| #define U8_TO_BUF_BE(buf, offset, value) \ |
| do { \ |
| unsigned char *buf_ = (unsigned char *)(buf); \ |
| int offset_ = (int)(offset); \ |
| u8 value_ = (u8)(value); \ |
| buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \ |
| buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \ |
| buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \ |
| buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \ |
| buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \ |
| buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \ |
| buf_[offset_ + 6] = (unsigned char)(value_ >> 8); \ |
| buf_[offset_ + 7] = (unsigned char)(value_ ); \ |
| } while (0) |
| |
| /* |
| * Initialize an hprof context struct. |
| * |
| * This will take ownership of "fileName" and "fp". |
| */ |
| void |
| hprofContextInit(hprof_context_t *ctx, char *fileName, FILE *fp, |
| bool writeHeader, bool directToDdms) |
| { |
| memset(ctx, 0, sizeof (*ctx)); |
| |
| if (directToDdms) { |
| /* |
| * Have to do this here, because it must happen after we |
| * memset the struct (want to treat fileDataPtr/fileDataSize |
| * as read-only while the file is open). |
| */ |
| assert(fp == NULL); |
| fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize); |
| if (fp == NULL) { |
| /* not expected */ |
| LOGE("hprof: open_memstream failed: %s\n", strerror(errno)); |
| dvmAbort(); |
| } |
| } |
| |
| ctx->directToDdms = directToDdms; |
| ctx->fileName = fileName; |
| ctx->fp = fp; |
| |
| ctx->curRec.allocLen = 128; |
| ctx->curRec.body = malloc(ctx->curRec.allocLen); |
| //xxx check for/return an error |
| |
| if (writeHeader) { |
| char magic[] = HPROF_MAGIC_STRING; |
| unsigned char buf[4]; |
| struct timeval now; |
| u8 nowMs; |
| |
| /* Write the file header. |
| * |
| * [u1]*: NUL-terminated magic string. |
| */ |
| fwrite(magic, 1, sizeof(magic), fp); |
| |
| /* u4: size of identifiers. We're using addresses |
| * as IDs, so make sure a pointer fits. |
| */ |
| U4_TO_BUF_BE(buf, 0, sizeof(void *)); |
| fwrite(buf, 1, sizeof(u4), fp); |
| |
| /* The current time, in milliseconds since 0:00 GMT, 1/1/70. |
| */ |
| if (gettimeofday(&now, NULL) < 0) { |
| nowMs = 0; |
| } else { |
| nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000; |
| } |
| |
| /* u4: high word of the 64-bit time. |
| */ |
| U4_TO_BUF_BE(buf, 0, (u4)(nowMs >> 32)); |
| fwrite(buf, 1, sizeof(u4), fp); |
| |
| /* u4: low word of the 64-bit time. |
| */ |
| U4_TO_BUF_BE(buf, 0, (u4)(nowMs & 0xffffffffULL)); |
| fwrite(buf, 1, sizeof(u4), fp); //xxx fix the time |
| } |
| } |
| |
| int |
| hprofFlushRecord(hprof_record_t *rec, FILE *fp) |
| { |
| if (rec->dirty) { |
| unsigned char headBuf[sizeof (u1) + 2 * sizeof (u4)]; |
| int nb; |
| |
| headBuf[0] = rec->tag; |
| U4_TO_BUF_BE(headBuf, 1, rec->time); |
| U4_TO_BUF_BE(headBuf, 5, rec->length); |
| |
| nb = fwrite(headBuf, 1, sizeof(headBuf), fp); |
| if (nb != sizeof(headBuf)) { |
| return UNIQUE_ERROR(); |
| } |
| nb = fwrite(rec->body, 1, rec->length, fp); |
| if (nb != (int)rec->length) { |
| return UNIQUE_ERROR(); |
| } |
| |
| rec->dirty = false; |
| } |
| //xxx if we used less than half (or whatever) of allocLen, shrink the buffer. |
| |
| return 0; |
| } |
| |
| int |
| hprofFlushCurrentRecord(hprof_context_t *ctx) |
| { |
| return hprofFlushRecord(&ctx->curRec, ctx->fp); |
| } |
| |
| int |
| hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time) |
| { |
| hprof_record_t *rec = &ctx->curRec; |
| int err; |
| |
| err = hprofFlushRecord(rec, ctx->fp); |
| if (err != 0) { |
| return err; |
| } else if (rec->dirty) { |
| return UNIQUE_ERROR(); |
| } |
| |
| rec->dirty = true; |
| rec->tag = tag; |
| rec->time = time; |
| rec->length = 0; |
| |
| return 0; |
| } |
| |
| static inline int |
| guaranteeRecordAppend(hprof_record_t *rec, size_t nmore) |
| { |
| size_t minSize; |
| |
| minSize = rec->length + nmore; |
| if (minSize > rec->allocLen) { |
| unsigned char *newBody; |
| size_t newAllocLen; |
| |
| newAllocLen = rec->allocLen * 2; |
| if (newAllocLen < minSize) { |
| newAllocLen = rec->allocLen + nmore + nmore/2; |
| } |
| newBody = realloc(rec->body, newAllocLen); |
| if (newBody != NULL) { |
| rec->body = newBody; |
| rec->allocLen = newAllocLen; |
| } else { |
| //TODO: set an error flag so future ops will fail |
| return UNIQUE_ERROR(); |
| } |
| } |
| |
| assert(rec->length + nmore <= rec->allocLen); |
| return 0; |
| } |
| |
| int |
| hprofAddU1ListToRecord(hprof_record_t *rec, const u1 *values, size_t numValues) |
| { |
| int err; |
| |
| err = guaranteeRecordAppend(rec, numValues); |
| if (err != 0) { |
| return err; |
| } |
| |
| memcpy(rec->body + rec->length, values, numValues); |
| rec->length += numValues; |
| |
| return 0; |
| } |
| |
| int |
| hprofAddU1ToRecord(hprof_record_t *rec, u1 value) |
| { |
| int err; |
| |
| err = guaranteeRecordAppend(rec, 1); |
| if (err != 0) { |
| return err; |
| } |
| |
| rec->body[rec->length++] = value; |
| |
| return 0; |
| } |
| |
| int |
| hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str) |
| { |
| /* The terminating NUL character is NOT written. |
| */ |
| //xxx don't do a strlen; add and grow as necessary, until NUL |
| return hprofAddU1ListToRecord(rec, (const u1 *)str, strlen(str)); |
| } |
| |
| int |
| hprofAddU2ListToRecord(hprof_record_t *rec, const u2 *values, size_t numValues) |
| { |
| unsigned char *insert; |
| size_t i; |
| int err; |
| |
| err = guaranteeRecordAppend(rec, numValues * 2); |
| if (err != 0) { |
| return err; |
| } |
| |
| //xxx this can be way smarter |
| //xxx also, don't do this bytewise if aligned and on a matching-endian arch |
| insert = rec->body + rec->length; |
| for (i = 0; i < numValues; i++) { |
| U2_TO_BUF_BE(insert, 0, *values++); |
| insert += sizeof(*values); |
| } |
| rec->length += numValues * 2; |
| |
| return 0; |
| } |
| |
| int |
| hprofAddU2ToRecord(hprof_record_t *rec, u2 value) |
| { |
| return hprofAddU2ListToRecord(rec, &value, 1); |
| } |
| |
| int |
| hprofAddU4ListToRecord(hprof_record_t *rec, const u4 *values, size_t numValues) |
| { |
| unsigned char *insert; |
| size_t i; |
| int err; |
| |
| err = guaranteeRecordAppend(rec, numValues * 4); |
| if (err != 0) { |
| return err; |
| } |
| |
| //xxx this can be way smarter |
| //xxx also, don't do this bytewise if aligned and on a matching-endian arch |
| insert = rec->body + rec->length; |
| for (i = 0; i < numValues; i++) { |
| U4_TO_BUF_BE(insert, 0, *values++); |
| insert += sizeof(*values); |
| } |
| rec->length += numValues * 4; |
| |
| return 0; |
| } |
| |
| int |
| hprofAddU4ToRecord(hprof_record_t *rec, u4 value) |
| { |
| return hprofAddU4ListToRecord(rec, &value, 1); |
| } |
| |
| int |
| hprofAddU8ListToRecord(hprof_record_t *rec, const u8 *values, size_t numValues) |
| { |
| unsigned char *insert; |
| size_t i; |
| int err; |
| |
| err = guaranteeRecordAppend(rec, numValues * 8); |
| if (err != 0) { |
| return err; |
| } |
| |
| //xxx this can be way smarter |
| //xxx also, don't do this bytewise if aligned and on a matching-endian arch |
| insert = rec->body + rec->length; |
| for (i = 0; i < numValues; i++) { |
| U8_TO_BUF_BE(insert, 0, *values++); |
| insert += sizeof(*values); |
| } |
| rec->length += numValues * 8; |
| |
| return 0; |
| } |
| |
| int |
| hprofAddU8ToRecord(hprof_record_t *rec, u8 value) |
| { |
| return hprofAddU8ListToRecord(rec, &value, 1); |
| } |