/*
* 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);
}