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
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
35 hprof_context_t *
hprofStartup(const char * outputFileName)36 hprofStartup(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 */
89 static bool
copyFileToFile(FILE * dstFp,FILE * srcFp)90 copyFileToFile(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
114 /*
115 * Finish up the hprof dump. Returns true on success.
116 */
117 bool
hprofShutdown(hprof_context_t * ctx)118 hprofShutdown(hprof_context_t *ctx)
119 {
120 FILE *tempFp = ctx->fp;
121 FILE *fp;
122
123 /* flush output to the temp file, then prepare the output file */
124 hprofFlushCurrentRecord(ctx);
125 free(ctx->curRec.body);
126 ctx->curRec.body = NULL;
127 ctx->curRec.allocLen = 0;
128 ctx->fp = NULL;
129
130 LOGI("hprof: dumping heap strings to \"%s\".\n", ctx->fileName);
131 fp = fopen(ctx->fileName, "w");
132 if (fp == NULL) {
133 LOGE("can't open %s: %s\n", ctx->fileName, strerror(errno));
134 fclose(tempFp);
135 free(ctx->fileName);
136 free(ctx);
137 return false;
138 }
139 hprofContextInit(ctx, ctx->fileName, fp, true);
140
141 hprofDumpStrings(ctx);
142 hprofDumpClasses(ctx);
143
144 /* Write a dummy stack trace record so the analysis
145 * tools don't freak out.
146 */
147 hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
148 hprofAddU4ToRecord(&ctx->curRec, HPROF_NULL_STACK_TRACE);
149 hprofAddU4ToRecord(&ctx->curRec, HPROF_NULL_THREAD);
150 hprofAddU4ToRecord(&ctx->curRec, 0); // no frames
151
152 #if WITH_HPROF_STACK
153 hprofDumpStackFrames(ctx);
154 hprofDumpStacks(ctx);
155 #endif
156
157 hprofFlushCurrentRecord(ctx);
158
159 hprofShutdown_Class();
160 hprofShutdown_String();
161 #if WITH_HPROF_STACK
162 hprofShutdown_Stack();
163 hprofShutdown_StackFrame();
164 #endif
165
166 /*
167 * Append the contents of the temp file to the output file. The temp
168 * file was removed immediately after being opened, so it will vanish
169 * when we close it.
170 */
171 rewind(tempFp);
172 if (!copyFileToFile(ctx->fp, tempFp)) {
173 LOGW("hprof: file copy failed, hprof data may be incomplete\n");
174 /* finish up anyway */
175 }
176
177 fclose(tempFp);
178 fclose(ctx->fp);
179 free(ctx->fileName);
180 free(ctx->curRec.body);
181 free(ctx);
182
183 /* throw out a log message for the benefit of "runhat" */
184 LOGI("hprof: heap dump completed, temp file removed\n");
185 return true;
186 }
187