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 */
64 void
hprofContextInit(hprof_context_t * ctx,char * fileName,int fd,bool writeHeader,bool directToDdms)65 hprofContextInit(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
129 int
hprofFlushRecord(hprof_record_t * rec,FILE * fp)130 hprofFlushRecord(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
156 int
hprofFlushCurrentRecord(hprof_context_t * ctx)157 hprofFlushCurrentRecord(hprof_context_t *ctx)
158 {
159 return hprofFlushRecord(&ctx->curRec, ctx->memFp);
160 }
161
162 int
hprofStartNewRecord(hprof_context_t * ctx,u1 tag,u4 time)163 hprofStartNewRecord(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
183 static inline int
guaranteeRecordAppend(hprof_record_t * rec,size_t nmore)184 guaranteeRecordAppend(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
211 int
hprofAddU1ListToRecord(hprof_record_t * rec,const u1 * values,size_t numValues)212 hprofAddU1ListToRecord(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
227 int
hprofAddU1ToRecord(hprof_record_t * rec,u1 value)228 hprofAddU1ToRecord(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
242 int
hprofAddUtf8StringToRecord(hprof_record_t * rec,const char * str)243 hprofAddUtf8StringToRecord(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
251 int
hprofAddU2ListToRecord(hprof_record_t * rec,const u2 * values,size_t numValues)252 hprofAddU2ListToRecord(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
275 int
hprofAddU2ToRecord(hprof_record_t * rec,u2 value)276 hprofAddU2ToRecord(hprof_record_t *rec, u2 value)
277 {
278 return hprofAddU2ListToRecord(rec, &value, 1);
279 }
280
281 int
hprofAddU4ListToRecord(hprof_record_t * rec,const u4 * values,size_t numValues)282 hprofAddU4ListToRecord(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
305 int
hprofAddU4ToRecord(hprof_record_t * rec,u4 value)306 hprofAddU4ToRecord(hprof_record_t *rec, u4 value)
307 {
308 return hprofAddU4ListToRecord(rec, &value, 1);
309 }
310
311 int
hprofAddU8ListToRecord(hprof_record_t * rec,const u8 * values,size_t numValues)312 hprofAddU8ListToRecord(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
335 int
hprofAddU8ToRecord(hprof_record_t * rec,u8 value)336 hprofAddU8ToRecord(hprof_record_t *rec, u8 value)
337 {
338 return hprofAddU8ListToRecord(rec, &value, 1);
339 }
340