blob: 0b95aad010c9d3ba09236720cc85ad6a6869abfe [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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
17/*
18 * Preparation and completion of hprof data generation. The output is
19 * written into two files and then combined. This is necessary because
20 * we generate some of the data (strings and classes) while we dump the
21 * heap, and some analysis tools require that the class and string data
22 * appear first.
23 */
24#include "Hprof.h"
25
26#include <string.h>
27#include <unistd.h>
28#include <errno.h>
29#include <sys/time.h>
30#include <time.h>
31
32
33#define kHeadSuffix "-hptemp"
34
35hprof_context_t *
Andy McFadden6bf992c2010-01-28 17:01:39 -080036hprofStartup(const char *outputFileName, bool directToDdms)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080037{
Andy McFadden6bf992c2010-01-28 17:01:39 -080038 FILE* fp = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080039
Andy McFadden6bf992c2010-01-28 17:01:39 -080040 if (!directToDdms) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080041 int len = strlen(outputFileName);
42 char fileName[len + sizeof(kHeadSuffix)];
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080043
44 /* Construct the temp file name. This wasn't handed to us by the
45 * application, so we need to be careful about stomping on it.
46 */
47 sprintf(fileName, "%s" kHeadSuffix, outputFileName);
48 if (access(fileName, F_OK) == 0) {
49 LOGE("hprof: temp file %s exists, bailing\n", fileName);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080050 return NULL;
51 }
52
53 fp = fopen(fileName, "w+");
54 if (fp == NULL) {
55 LOGE("hprof: can't open %s: %s.\n", fileName, strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080056 return NULL;
57 }
58 if (unlink(fileName) != 0) {
59 LOGW("hprof: WARNING: unable to remove temp file %s\n", fileName);
60 /* keep going */
61 }
62 LOGI("hprof: dumping VM heap to \"%s\".\n", fileName);
Andy McFadden6bf992c2010-01-28 17:01:39 -080063 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080064
Andy McFadden6bf992c2010-01-28 17:01:39 -080065 hprofStartup_String();
66 hprofStartup_Class();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080067#if WITH_HPROF_STACK
Andy McFadden6bf992c2010-01-28 17:01:39 -080068 hprofStartup_StackFrame();
69 hprofStartup_Stack();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080070#endif
71
Andy McFadden6bf992c2010-01-28 17:01:39 -080072 hprof_context_t *ctx = malloc(sizeof(*ctx));
73 if (ctx == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080074 LOGE("hprof: can't allocate context.\n");
Andy McFadden6bf992c2010-01-28 17:01:39 -080075 if (fp != NULL)
76 fclose(fp);
77 return NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080078 }
79
Andy McFadden6bf992c2010-01-28 17:01:39 -080080 /* pass in "fp" for the temp file, and the name of the output file */
81 hprofContextInit(ctx, strdup(outputFileName), fp, false, directToDdms);
82
83 assert(ctx->fp != NULL);
84
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080085 return ctx;
86}
87
88/*
89 * Copy the entire contents of "srcFp" to "dstFp".
90 *
91 * Returns "true" on success.
92 */
93static bool
94copyFileToFile(FILE *dstFp, FILE *srcFp)
95{
96 char buf[65536];
97 size_t dataRead, dataWritten;
98
99 while (true) {
100 dataRead = fread(buf, 1, sizeof(buf), srcFp);
101 if (dataRead > 0) {
102 dataWritten = fwrite(buf, 1, dataRead, dstFp);
103 if (dataWritten != dataRead) {
104 LOGE("hprof: failed writing data (%d of %d): %s\n",
105 dataWritten, dataRead, strerror(errno));
106 return false;
107 }
108 } else {
109 if (feof(srcFp))
110 return true;
111 LOGE("hprof: failed reading data (res=%d): %s\n",
112 dataRead, strerror(errno));
113 return false;
114 }
115 }
116}
117
The Android Open Source Project99409882009-03-18 22:20:24 -0700118/*
119 * Finish up the hprof dump. Returns true on success.
120 */
121bool
Andy McFadden6bf992c2010-01-28 17:01:39 -0800122hprofShutdown(hprof_context_t *tailCtx)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800123{
Andy McFadden6bf992c2010-01-28 17:01:39 -0800124 FILE *fp = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800125
126 /* flush output to the temp file, then prepare the output file */
Andy McFadden6bf992c2010-01-28 17:01:39 -0800127 hprofFlushCurrentRecord(tailCtx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800128
Andy McFadden6bf992c2010-01-28 17:01:39 -0800129 LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName);
130 if (!tailCtx->directToDdms) {
131 fp = fopen(tailCtx->fileName, "w");
132 if (fp == NULL) {
133 LOGE("can't open %s: %s\n", tailCtx->fileName, strerror(errno));
134 hprofFreeContext(tailCtx);
135 return false;
136 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800137 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800138
Andy McFadden6bf992c2010-01-28 17:01:39 -0800139 /*
140 * Create a new context struct for the start of the file. We
141 * heap-allocate it so we can share the "free" function.
142 */
143 hprof_context_t *headCtx = malloc(sizeof(*headCtx));
144 if (headCtx == NULL) {
145 LOGE("hprof: can't allocate context.\n");
146 if (fp != NULL)
147 fclose(fp);
148 hprofFreeContext(tailCtx);
149 return NULL;
150 }
151 hprofContextInit(headCtx, strdup(tailCtx->fileName), fp, true,
152 tailCtx->directToDdms);
153
154 hprofDumpStrings(headCtx);
155 hprofDumpClasses(headCtx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800156
157 /* Write a dummy stack trace record so the analysis
158 * tools don't freak out.
159 */
Andy McFadden6bf992c2010-01-28 17:01:39 -0800160 hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
161 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
162 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
163 hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800164
165#if WITH_HPROF_STACK
Andy McFadden6bf992c2010-01-28 17:01:39 -0800166 hprofDumpStackFrames(headCtx);
167 hprofDumpStacks(headCtx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800168#endif
169
Andy McFadden6bf992c2010-01-28 17:01:39 -0800170 hprofFlushCurrentRecord(headCtx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800171
172 hprofShutdown_Class();
173 hprofShutdown_String();
174#if WITH_HPROF_STACK
175 hprofShutdown_Stack();
176 hprofShutdown_StackFrame();
177#endif
178
Andy McFadden6bf992c2010-01-28 17:01:39 -0800179 if (tailCtx->directToDdms) {
180 /* flush to ensure memstream pointer and size are updated */
181 fflush(headCtx->fp);
182 fflush(tailCtx->fp);
183
184 /* send the data off to DDMS */
185 struct iovec iov[2];
186 iov[0].iov_base = headCtx->fileDataPtr;
187 iov[0].iov_len = headCtx->fileDataSize;
188 iov[1].iov_base = tailCtx->fileDataPtr;
189 iov[1].iov_len = tailCtx->fileDataSize;
190 dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
191 } else {
192 /*
193 * Append the contents of the temp file to the output file. The temp
194 * file was removed immediately after being opened, so it will vanish
195 * when we close it.
196 */
197 rewind(tailCtx->fp);
198 if (!copyFileToFile(headCtx->fp, tailCtx->fp)) {
199 LOGW("hprof: file copy failed, hprof data may be incomplete\n");
200 /* finish up anyway */
201 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800202 }
203
Andy McFadden6bf992c2010-01-28 17:01:39 -0800204 hprofFreeContext(headCtx);
205 hprofFreeContext(tailCtx);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800206
207 /* throw out a log message for the benefit of "runhat" */
208 LOGI("hprof: heap dump completed, temp file removed\n");
The Android Open Source Project99409882009-03-18 22:20:24 -0700209 return true;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800210}
Andy McFadden6bf992c2010-01-28 17:01:39 -0800211
212/*
213 * Free any heap-allocated items in "ctx", and then free "ctx" itself.
214 */
215void
216hprofFreeContext(hprof_context_t *ctx)
217{
218 assert(ctx != NULL);
219
220 if (ctx->fp != NULL)
221 fclose(ctx->fp);
222 free(ctx->curRec.body);
223 free(ctx->fileName);
224 free(ctx->fileDataPtr);
225 free(ctx);
226}