• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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