blob: 66b46f485aff514d23d6431bdfef4972fddefba1 [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 *
36hprofStartup(const char *outputFileName)
37{
38 hprof_context_t *ctx;
39
40 ctx = malloc(sizeof(*ctx));
41 if (ctx != NULL) {
42 int len = strlen(outputFileName);
43 char fileName[len + sizeof(kHeadSuffix)];
44 FILE *fp;
45
46 /* Construct the temp file name. This wasn't handed to us by the
47 * application, so we need to be careful about stomping on it.
48 */
49 sprintf(fileName, "%s" kHeadSuffix, outputFileName);
50 if (access(fileName, F_OK) == 0) {
51 LOGE("hprof: temp file %s exists, bailing\n", fileName);
52 free(ctx);
53 return NULL;
54 }
55
56 fp = fopen(fileName, "w+");
57 if (fp == NULL) {
58 LOGE("hprof: can't open %s: %s.\n", fileName, strerror(errno));
59 free(ctx);
60 return NULL;
61 }
62 if (unlink(fileName) != 0) {
63 LOGW("hprof: WARNING: unable to remove temp file %s\n", fileName);
64 /* keep going */
65 }
66 LOGI("hprof: dumping VM heap to \"%s\".\n", fileName);
67
68 hprofStartup_String();
69 hprofStartup_Class();
70#if WITH_HPROF_STACK
71 hprofStartup_StackFrame();
72 hprofStartup_Stack();
73#endif
74
75 /* pass in "fp" for the temp file, and the name of the output file */
76 hprofContextInit(ctx, strdup(outputFileName), fp, false);
77 } else {
78 LOGE("hprof: can't allocate context.\n");
79 }
80
81 return ctx;
82}
83
84/*
85 * Copy the entire contents of "srcFp" to "dstFp".
86 *
87 * Returns "true" on success.
88 */
89static bool
90copyFileToFile(FILE *dstFp, FILE *srcFp)
91{
92 char buf[65536];
93 size_t dataRead, dataWritten;
94
95 while (true) {
96 dataRead = fread(buf, 1, sizeof(buf), srcFp);
97 if (dataRead > 0) {
98 dataWritten = fwrite(buf, 1, dataRead, dstFp);
99 if (dataWritten != dataRead) {
100 LOGE("hprof: failed writing data (%d of %d): %s\n",
101 dataWritten, dataRead, strerror(errno));
102 return false;
103 }
104 } else {
105 if (feof(srcFp))
106 return true;
107 LOGE("hprof: failed reading data (res=%d): %s\n",
108 dataRead, strerror(errno));
109 return false;
110 }
111 }
112}
113
114void
115hprofShutdown(hprof_context_t *ctx)
116{
117 FILE *tempFp = ctx->fp;
118 FILE *fp;
119
120 /* flush output to the temp file, then prepare the output file */
121 hprofFlushCurrentRecord(ctx);
122 free(ctx->curRec.body);
123 ctx->curRec.body = NULL;
124 ctx->curRec.allocLen = 0;
125 ctx->fp = NULL;
126
127 LOGI("hprof: dumping heap strings to \"%s\".\n", ctx->fileName);
128 fp = fopen(ctx->fileName, "w");
129 if (fp == NULL) {
130 LOGE("can't open %s: %s\n", ctx->fileName, strerror(errno));
131 fclose(tempFp);
132 free(ctx->fileName);
133 free(ctx);
134 return;
135 }
136 hprofContextInit(ctx, ctx->fileName, fp, true);
137
138 hprofDumpStrings(ctx);
139 hprofDumpClasses(ctx);
140
141 /* Write a dummy stack trace record so the analysis
142 * tools don't freak out.
143 */
144 hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
145 hprofAddU4ToRecord(&ctx->curRec, HPROF_NULL_STACK_TRACE);
146 hprofAddU4ToRecord(&ctx->curRec, HPROF_NULL_THREAD);
147 hprofAddU4ToRecord(&ctx->curRec, 0); // no frames
148
149#if WITH_HPROF_STACK
150 hprofDumpStackFrames(ctx);
151 hprofDumpStacks(ctx);
152#endif
153
154 hprofFlushCurrentRecord(ctx);
155
156 hprofShutdown_Class();
157 hprofShutdown_String();
158#if WITH_HPROF_STACK
159 hprofShutdown_Stack();
160 hprofShutdown_StackFrame();
161#endif
162
163 /*
164 * Append the contents of the temp file to the output file. The temp
165 * file was removed immediately after being opened, so it will vanish
166 * when we close it.
167 */
168 rewind(tempFp);
169 if (!copyFileToFile(ctx->fp, tempFp)) {
170 LOGW("hprof: file copy failed, hprof data may be incomplete\n");
171 /* finish up anyway */
172 }
173
174 fclose(tempFp);
175 fclose(ctx->fp);
176 free(ctx->fileName);
177 free(ctx->curRec.body);
178 free(ctx);
179
180 /* throw out a log message for the benefit of "runhat" */
181 LOGI("hprof: heap dump completed, temp file removed\n");
182}